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

Sentry-graphql does not send any GraphQL operations as transactions #3277

Open
Andrevmatias opened this issue Mar 22, 2024 · 6 comments
Open
Labels
Platform: Java Type: Bug Something isn't working

Comments

@Andrevmatias
Copy link

Andrevmatias commented Mar 22, 2024

Integration

sentry-graphql

Java Version

17

Version

7.6.0

Steps to Reproduce

Using Spring Boot 2.7.8 and the spring boot sentry integration.

Instrumentation configuration:

@Configuration
class SentryConfiguration(
    val tracingDataBuilder: TracingDataBuilder
) {
    @Bean
    fun sentryTracingInstrumentation() = SentryInstrumentation(
        { span: ISpan, env: DataFetchingEnvironment, _: Any? ->
            tracingDataBuilder.createTags(env.operationDefinition.operation.toString()).forEach { (key, value) ->
                span.setTag(key, value)
            }
            span
        },
        SentrySpringSubscriptionHandler(),
        false
    )

    @Bean
    fun beforeSendTransactionCallback() = BeforeSendTransactionCallback { sentryTransaction, _ ->
        if (excludedRoutes.contains(sentryTransaction.transaction)) {
            null
        } else {
            sentryTransaction
        }
    }
}

application.properties

sentry.enabled=true
sentry.debug=true
sentry.dsn=[dsn]
sentry.release=@project.version@
sentry.environment=@spring.profiles.active@
sentry.traces-sample-rate=1.0

Expected Result

Sentry should send all GraphQL requests as transactions and spans for each query/mutation field.

Actual Result

No GraphQL transaction is being sent. Other transactions are working fine. The Debug info also does not show any GraphQL transaction.

Trying to figure out what was happening I investigated the SentryInstrumentation class and couldn't find where the transaction is supposed to be finished. I only found routines to finish the spans.

@adinauer
Copy link
Member

Hello @Andrevmatias I just tested using our Spring Boot 2 sample and there's a transaction for the request which the GraphQL span is attached to. Maybe you can spot a difference to your setup by looking at our sample.

You can also attach a debugger to SentryInstrumentation line number 306 and check if there's an actual transaction there or if it's a NoOpTransaction.

@Andrevmatias
Copy link
Author

Thanks @adinauer!

The only difference I noticed is that we use the graphql.kickstart.tools lib and don't specify the sentry.enable-tracing=true property (as it's deprecated).

The transaction value at line 306 is an instance of SentryTracer. It's finish method is never called. Isn't it the method responsible to send the event? So, these events are never sent to the sentry server, while "on demand" events are working fine.

@adinauer
Copy link
Member

@Andrevmatias finish should be called by

if you're using Spring Boot 2 WebMVC.

Can you add a breakpoint there and see what's happening?

@Andrevmatias
Copy link
Author

Andrevmatias commented Mar 29, 2024

It never reaches this line while resolving GraphQL requests because transactionName is null.

It happens because the request does not have the BEST_MATCHING_PATTERN_ATTRIBUTE set. It seems that this attribute is mandatory for the SpringMvcTransactionNameProvider to work.

Looking at the code, even if this attribute is set, all the GraphQL requests would be grouped into the POST /api/graphql transaction, right? I thought every query/mutation would be in a separate transaction. Is this possible?

I implemented a custom TransactionNameProvider that returns a fixed name and it worked fine, but I couldn't find a way to give the transaction the name of the GraphQL operation.

So, two questions:

  1. Is there any configuration step I missed to make BEST_MATCHING_PATTERN_ATTRIBUTE to be set? I can't find anything in the docs.
  2. Is it possible to separate every GraphQL operation in its own transaction?

@adinauer
Copy link
Member

adinauer commented Apr 2, 2024

@Andrevmatias

  1. Is there any configuration step I missed to make BEST_MATCHING_PATTERN_ATTRIBUTE to be set? I can't find anything in the docs.

There's cases where this attribute is not set, it seems your setup is one of them (Maybe because you don't have Spring WebMVC?). Event then yes, it'd only set POST /api/graphql.

I thought every query/mutation would be in a separate transaction. Is this possible?

  1. Is it possible to separate every GraphQL operation in its own transaction?

Currently no.

I think you best option at the moment is to implement your own TransactionNameProvider and return the name of the query from it by extracting it from the HttpServletRequest. You'll likely have to parse the request. Not quite sure what to do with multiple operations in one request - maybe you can just concat them.

Sorry I don't have a better solution at the moment. If something comes to mind, I'll update here.

The following will require some SDK changes.
Ideas to make changing the name easier in the future:

  • allow setting transaction name in beforeSendTransaction
  • pass more details into TransactionNameProvider (e.g. pass in the transaction)

Ideas to make promoting spans to transactions easier in the future:

  • when using OTEL for performance add some flag that tells our SpanProcessor/SpanExporter to convert a span to a transaction.

@Andrevmatias
Copy link
Author

Andrevmatias commented Apr 2, 2024

There's cases where this attribute is not set, it seems your setup is one of them (Maybe because you don't have Spring >WebMVC?). Event then yes, it'd only set POST /api/graphql.

I do have Spring WebMVC. Calls to REST Controllers are sent correctly but I think the attribute is not set when using the graphql.kickstart.tools structure.

For future reference, my solution was to implement my own TransactionNameProvider:

@Bean
fun transactionNameProvider() = TransactionNameProvider {
    val pattern = it.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE) as String?
    "${it.method} ${pattern ?: it.requestURI}"
}

About splitting into multiple transactions:

As the transactions were not being finished, I implemented a custom SentryInstrumentation that starts the transaction in beginExecution and finishes it in instrumentExecutionResult. It seems to work as expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Platform: Java Type: Bug Something isn't working
Projects
Status: No status
Status: Needs Discussion
Development

No branches or pull requests

3 participants