open-telemetry / opentelemetry-js-contrib

OpenTelemetry instrumentation for JavaScript modules
https://opentelemetry.io
Apache License 2.0
703 stars 518 forks source link

instrumentation-graphql produces only a single graphql trace #1660

Closed chemicstry closed 1 year ago

chemicstry commented 1 year ago

What version of OpenTelemetry are you using?

├── @autotelic/fastify-opentelemetry@0.17.1
├── @opentelemetry/api@1.4.1
├── @opentelemetry/auto-instrumentations-node@0.39.1
├── @opentelemetry/context-async-hooks@1.15.2
├── @opentelemetry/core@1.15.2
├── @opentelemetry/exporter-prometheus@0.41.2
├── @opentelemetry/exporter-trace-otlp-http@0.41.2
├── @opentelemetry/instrumentation-graphql@0.35.1
├── @opentelemetry/sdk-metrics@1.15.2
├── @opentelemetry/sdk-node@0.41.2
├── fastify@4.21.0
├── graphql-scalars@1.22.2
├── graphql@16.7.1
├── mercurius-cache@5.0.0
├── mercurius@12.2.0
├── nexus@1.3.0
├── ts-node-dev@2.0.0
├── typescript@5.1.6

What version of Node are you using?

Node v18.15.0

What did you do?

I'm trying to trace fastify/mercurius graphql requests by feeding opentelemetry data into a local jaeger instance.

I initialize open telemetry with:

import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { CompositePropagator, W3CTraceContextPropagator } from '@opentelemetry/core';
import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks';

const sdk = new NodeSDK({
    serviceName: "app",
    traceExporter: new OTLPTraceExporter({
        url: process.env.OPEN_TELEMETRY_OTLP_EXPORTER_URL
    }),
    contextManager: new AsyncHooksContextManager(),
    textMapPropagator: new CompositePropagator({
        propagators: [
            new W3CTraceContextPropagator(),
        ]
    }),
    instrumentations: [
        getNodeAutoInstrumentations(),
    ],
});

sdk.start();

I made sure that this is the first piece of code to be imported in main.ts.

What did you expect to see?

A separate trace for each graphql query <graphql_query_name> operation.

What did you see instead?

Only the first trace is generated for each graphql query type. Subsequent queries are not shown. I do, however, see multiple queries under POST /graphql, but not under graphql.resolve or query <graphql_query_name>.

Restarting the server and querying graphql generates a single trace again.

Additional context

I'm new to opentelemetry and it seems a bit hard to understand with all the component auto registration. Any help debugging this would be appreciated.

dyladan commented 1 year ago

Can you please provide a minimum reproduction code sample so we can help resolve this?

m-hammad01 commented 1 year ago

Facing this exact same issue

m-hammad01 commented 1 year ago

Following is my instrumentation code

import path from 'path';

import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-proto';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';
import { GraphQLInstrumentation } from '@opentelemetry/instrumentation-graphql';
import { GrpcInstrumentation } from '@opentelemetry/instrumentation-grpc';
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core';
import { Resource } from '@opentelemetry/resources';
import {
  ConsoleMetricExporter,
  PeriodicExportingMetricReader,
} from '@opentelemetry/sdk-metrics';
import { NodeSDK } from '@opentelemetry/sdk-node';
import { ConsoleSpanExporter } from '@opentelemetry/sdk-trace-base';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import { config } from 'dotenv';

const customResponseHook = (span: any, data: any) => {
   span.updateName(span.attributes['graphql.operation.name']);
};

// explicitly load .env file
config({
  path: path.join(process.cwd(), `.env.${process.env.NODE_ENV}`),
});

const traceExporter = (url: string | undefined) => {
  return new OTLPTraceExporter({ url });
  //  return new ConsoleSpanExporter(); 
};

const metricReader = (url: string | undefined) => {
  if (url)
     return new PeriodicExportingMetricReader({
       exporter: new OTLPMetricExporter({ url }),
     });
   else
     return new PeriodicExportingMetricReader({
       exporter: new ConsoleMetricExporter(),
     });

};

export const openTelemetrySDK = new NodeSDK({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'tools-service',
  }),
  traceExporter: traceExporter(process.env.OPEN_TELEMETRY_TRACES_EXPORTER_URL),
  metricReader: metricReader(process.env.OPEN_TELEMETRY_METRICS_EXPORTER_URL),
  instrumentations: [
     new HttpInstrumentation(),
     new ExpressInstrumentation(),
    new GraphQLInstrumentation({
      allowValues: true,
      responseHook: customResponseHook,
    }),
     new NestInstrumentation(),
    new GrpcInstrumentation(),
  ],
});
chemicstry commented 1 year ago

So I finally got time to create a minimum reproduction code and couldn't reproduce the issue. I then tested this on my original project where I first observed the issue and it was gone too. The only difference was an npm install, which probably updated otel dependencies. I assume that this was inadvertently fixed in some commit, so I'm closing the issue.