Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenTelemetry plugin #2255

Open
darren-west opened this issue Jun 18, 2024 · 0 comments
Open

OpenTelemetry plugin #2255

darren-west opened this issue Jun 18, 2024 · 0 comments

Comments

@darren-west
Copy link
Contributor

Summary

The current implementation of the OpenTelemetry plugin can lead to confusing spans because the execution span starts within the OpenTelemetry plugin but these do not wrap each other and instead run as part of an iteration over the plugins.

I think the correct way to handle OpenTelemetry traces within envelop is to wrap the GraphQL functions with spans, for example the executeFn and subscribeFn, this will effectively instrument the GraphQL phases.

A plugin that wants to add instrumentation can pick up the parent context and add a span which will appear alongside any other instrumented plugin in terms of the traces.

An example of wrapping the execute function would be like so:

      setExecuteFn(async () => {
        const span = tracer.startSpan(`${operationType}.${operationName}`, {}) // todo: fill in attrbutes.

        return await opentelemetry.context.with(api.context.active(), async () => {
          let result: ExecutionResult;
          try {
            result = await execute(args);
            if (isAsyncIterable(result)) {
              return mapAsyncIterator(result[Symbol.asyncIterator](), (next: ExecutionResult) => next, (error: GraphQLError) => markError(executionSpan, {errors: [error]}), () => span.end());
            }
          }finally {
            if (!isAsyncIterable(result)){
              span.end();
            }
          }
          return result;
        });
      });

This could be repeated for all phases of the GraphQL request and gives a more realistic view of how the process is executed.

Further to this we could create a Yoga Plugin that takes this further and wraps the request handler so that the parent context is set through all phases.

onRequest: ({ request, setRequestHandler, requestHandler }) => {
   setRequestHandler((req, serverCtx) => {
     const span = tracer.startSpan("yoga.request");
     const result = api.context.with(api.trace.setSpan(api.context.active(), span), () => requestHandler(req, serverCtx));        
     span.end();
   })
 },

I think this would provide a much better instrumentation of GraphQL and how it is executed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant