Springboot Kickstart GraphQL - Metrics for number of requests per resolver

354 Views Asked by At

I am currently trying in SpringBoot GraphQL kickstart to track the number of times each resolver method is called. To be more specific, I want to exactly how many times the methods of my GraphQLResolver<T> are called. This would have two utilities:

  • Track if the deprecated resolvers are still used
  • Know which fields are the most used, in order to optimize the database queries for those

To do so, I implemented a really weird and not-so-clean way using schema directive wiring.

@Component
class ResolverUsageCountInstrumentation(
    private val meterRegistry: MeterRegistry
) : SchemaDirectiveWiring {
    private val callsRecordingMap = ConcurrentHashMap<String, Int>()

    override fun onField(environment: SchemaDirectiveWiringEnvironment<GraphQLFieldDefinition>): GraphQLFieldDefinition {
        val fieldContainer = environment.fieldsContainer
        val fieldDefinition = environment.fieldDefinition

        val currentDF = environment.codeRegistry.getDataFetcher(fieldContainer, fieldDefinition)
        if (currentDF.javaClass.name != "graphql.kickstart.tools.resolver.MethodFieldResolverDataFetcher") {
            return fieldDefinition
        }

        val signature = getMethodSignature(unwrappedDF)
        callsRecordingMap[signature] = 0

        val newDF = DataFetcherFactories.wrapDataFetcher(currentDF) { dfe: DataFetchingEnvironment, value: Any? ->
            callsRecordingMap.computeIfPresent(signature) { _, current: Int -> current + 1 }
            value
        }

        environment.codeRegistry.dataFetcher(fieldContainer, fieldDefinition, newDF)

        return fieldDefinition
    }

    private fun getMethodSignature(currentDF: DataFetcher<*>): String {
        val method = getFieldVal(currentDF, "resolverMethod", true) as Method // nonapi.io.github.classgraph.utils.ReflectionUtils
        return "${method.declaringClass.name}#${method.name}"
    }
}

This technique does the work, but has the big disadvantage of not working if the data fetcher is wrapped. Along with that, it's not really clean at all. I'm wondering, would there be a better way to do this?

Thank you!

0

There are 0 best solutions below