Skip to content

OpenTelemetry plugin #2255

Open
Open
@darren-west

Description

@darren-west

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions