Netflix / dgs-federation-example

Apache License 2.0
108 stars 43 forks source link

Enable Tracing in Apollo Studio #4

Open sbilello opened 3 years ago

sbilello commented 3 years ago

Screen Shot 2021-05-03 at 7 43 11 PM I was playing with this demo and I noticed that the traces are not available. Is there some parameter or does need to be implemented the exposure of the fields for the tracing? Please check the apollo-tracing specification and the relative PR GraphQL-Java-PR

paulbakker commented 3 years ago

graphql-java comes with an instrumentation class graphql.execution.instrumentation.tracing.TracingInstrumentation for this already as described here.

If you register this class using a @Bean in your own code, the DGS framework will pick it up automatically.

paulbakker commented 3 years ago

I only now realize this issue is on the example app, and not on the framework itself. So yes, we should do exactly what I said in the previous reply for the example :-)

berngp commented 3 years ago

@sbilello sample PR https://github.com/Netflix/dgs-federation-example/pull/7/files

sbilello commented 3 years ago

Thanks @berngp and @paulbakker ! I added some breakpoint to verify that the extensions are added but maybe there is something missing on the apollo-studio side of things because I don't see the tracing breakdown Screen Shot 2021-05-11 at 2 12 53 PM

query TracingTest {
  shows {
    id
    reviews {
      __typename
      starRating
    }
  }
}

I was reading more about it in this https://www.apollographql.com/docs/federation/metrics/#gatsby-focus-wrapper and it looks like that it is not enough for apollo studio to make it work.

Accordingly to their documentation they require an ftv1 field

{
  "data": {
    "shows": [
      {
        "title": "Stranger Things"
      },
      {
        "title": "Ozark"
      },
      {
        "title": "The Crown"
      },
      {
        "title": "Dead to Me"
      },
      {
        "title": "Orange is the New Black"
      }
    ]
  },
  "extensions": {
    "tracing": {
      "version": 1,
      "startTime": "2021-05-11T21:40:35.438314Z",
      "endTime": "2021-05-11T21:40:35.443742Z",
      "duration": 5439178,
      "parsing": {
        "startOffset": 3342104,
        "duration": 3316764
      },
      "validation": {
        "startOffset": 3542869,
        "duration": 166042
      },
      "execution": {
        "resolvers": [
          {
            "path": [
              "shows"
            ],
            "parentType": "Query",
            "returnType": "[Show]",
            "fieldName": "shows",
            "startOffset": 3665287,
            "duration": 961267
          },
          {
            "path": [
              "shows",
              0,
              "title"
            ],
            "parentType": "Show",
            "returnType": "String",
            "fieldName": "title",
            "startOffset": 4706215,
            "duration": 252633
          },
          {
            "path": [
              "shows",
              1,
              "title"
            ],
            "parentType": "Show",
            "returnType": "String",
            "fieldName": "title",
            "startOffset": 5052506,
            "duration": 11554
          },
          {
            "path": [
              "shows",
              2,
              "title"
            ],
            "parentType": "Show",
            "returnType": "String",
            "fieldName": "title",
            "startOffset": 5128882,
            "duration": 12249
          },
          {
            "path": [
              "shows",
              3,
              "title"
            ],
            "parentType": "Show",
            "returnType": "String",
            "fieldName": "title",
            "startOffset": 5164359,
            "duration": 7933
          },
          {
            "path": [
              "shows",
              4,
              "title"
            ],
            "parentType": "Show",
            "returnType": "String",
            "fieldName": "title",
            "startOffset": 5195543,
            "duration": 7960
          }
        ]
      }
    }
  }
}
berngp commented 3 years ago

I think we need to explicitly enable the feature in the Apollo Server. ref. https://github.com/Netflix/dgs-federation-example/pull/8

sbilello commented 3 years ago

I tested this one but it is not enough to make it work. I need to find some time to try this project: https://github.com/braintree/apollo-tracing-uploader-java Thanks @berngp

sbilello commented 3 years ago

Finally I found the time to integrate the apollo-tracing-uploader-java

[Disclaimer: Not polished code] I had to add just one simple class like this one:

package us.test.graphql.server.instrumentation;

import com.braintreepayments.apollo_tracing_uploader.TracingUploadInstrumentation;
import com.braintreepayments.apollo_tracing_uploader.VariablesSanitizer;
import com.braintreepayments.apollo_tracing_uploader.impl.HttpTracingUploader;
import com.braintreepayments.apollo_tracing_uploader.impl.ScheduledBatchingTraceProducer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Duration;
import java.util.concurrent.Executors;

@Configuration
public class ApolloTracingUploadInstrumentation {

    @Value("${apollo.secret}")
    private String apolloSecret;

    @Bean
    public TracingUploadInstrumentation tracingUploadInstrumentation(ScheduledBatchingTraceProducer producer) {
        return TracingUploadInstrumentation.newBuilder()
                .sendTracesIf(() -> true)
                .customizeTrace((trace, context) -> trace.setClientName("testClient " + context.toString()))
                .sanitizeVariables(VariablesSanitizer.valuesTo("X"))
                .producer(producer)
                .build();
    }

    @Bean
    public ScheduledBatchingTraceProducer scheduledBatchingTraceProducer(HttpTracingUploader httpTracingUploader) {
        return ScheduledBatchingTraceProducer.newBuilder().batchingWindow(Duration.ofSeconds(1))
                .threadPoolSize(1)
                .customizeHeader(header -> header.setService("test-graphql-server"))
                .uploader(httpTracingUploader)
                .build();
    }

    @Bean
    public HttpTracingUploader httpTracingUploader() {
        return HttpTracingUploader.newBuilder()
                .executor(Executors.newSingleThreadScheduledExecutor())
                .apiKey(apolloSecret)
                .retries(3)
                .retryDelay(Duration.ofSeconds(5))
                .connectTimeout(Duration.ofSeconds(5))
                .readTimeout(Duration.ofSeconds(5))
                .build();
    }

}

There is no documentation on how to integrate it. I had to read the source code so I hope this snippet can save somebody some time!

CC: @berngp @srinivasankavitha @paulbakker

Screen Shot 2021-06-08 at 7 05 53 PM

Last but not least: I also enabled the tracing: true flag in the apollo-server for the gateway. https://github.com/apollographql/apollo-server/blob/main/packages/apollo-server-core/src/graphqlOptions.ts#L58

Do you think that would it be worth creating a dedicated module to integrate tracing with apollo or document this approach?

jonckvanderkogel commented 3 years ago

It's a bit easier than that. What you need to do is first import this dependency (I'm using version 0.6.4):

<dependency>
    <groupId>com.apollographql.federation</groupId>
    <artifactId>federation-graphql-java-support</artifactId>
    <version>${graphql.federation.version}</version>
</dependency>

Next, expose a bean of type: com.apollographql.federation.graphqljava.tracing.FederatedTracingInstrumentation

For example as follows:

import com.apollographql.federation.graphqljava.tracing.FederatedTracingInstrumentation;
import graphql.execution.instrumentation.SimpleInstrumentation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class InstrumentationConfig {
    @Bean
    public SimpleInstrumentation tracingInstrumentation() {
        return new FederatedTracingInstrumentation();
    }
}

Now the DGS framework will pick up this bean and voila, tracing has been enabled in Apollo Studio.

Check out a more complete working example here.