I've been working on adding monitoring metrics in our GraphQL gateway recently.
We're using graphql-spring-boot starter for the gateway.
After reading the following documentations, I manage to send the basic graphql.timer.query.* metrics to Datadog
What I've achieved so far is, when I send a GraphQL query/mutation, I'd collect the request count and time accordingly. e.g. sending the query below
query HelloWorldQuery {
greeting(
name: "Bob"
) {
message
}
}
I'll see metrics graphql.timer.query.count
/ graphql.timer.query.sum
with tags operationName=HelloWorldQuery
It works like perfectly, until I want to test a query with errors. I realise there is no metrics/tags related to a failed query. For example, if I the above query returns null data and some GraphQL errors, I'd still collect graphql.timer.query.count (operationName=HelloWorldQuery)
, but there's no additional tags for me to tell there is an error for that query.
In the gateway, I have implemented a custom GraphQLErrorHandler
, so I was thinking maybe I should add error counter (via MeterRegistry) in that class, but I am unable to get the operationName
simply from GraphQLError type. the best I can get is error.getPath() which gives the method name (e.g. greeting
) rather than the custom query name (HelloWorldQuery
- to be consistent with what graphql.timer.query.*
provides).
My question is, how to solve the above problem? And generally what is the best way of collecting GraphQL query metrics (including errors)?
------------------- Update -------------------
2019-12-31 I read a bit more about GraphQL Instrumentation here and checked the MetricsInstrumentation implementation in graphql-spring-boot repo, the I have an idea of extending the MetricsInstrumentation class by adding error metrics there.
2020-01-02 I tried to ingest my CustomMetricsInstrumentation class, but with no luck. There is internal AutoConfiguration wiring, which I cannot insert my auto configuration in the middle.
You can override the default TracingInstrumentation
with your own implementation. It will be picked automatically due to the @ConditionalOnMissingBean annotation in the GraphQLInstrumentationAutoConfiguration class. Here is a simple example that adds two custom metrics: graphql.counter.query.success and graphql.counter.query.error:
@Component
public class CustomMetricsInstrumentation extends TracingInstrumentation {
private static final String QUERY_STATUS_COUNTER_METRIC_NAME = "graphql.counter.query";
private static final String OPERATION_NAME_TAG = "operationName";
private static final String UNKNOWN_OPERATION_NAME = "unknown";
private MeterRegistry meterRegistry;
public CustomMetricsInstrumentation(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@Override
public CompletableFuture<ExecutionResult> instrumentExecutionResult(ExecutionResult executionResult,
InstrumentationExecutionParameters parameters) {
String status = CollectionUtils.isEmpty(executionResult.getErrors()) ? "success" : "error";
String operation = parameters.getOperation() != null ? parameters.getOperation() : UNKNOWN_OPERATION_NAME;
Collection<Tag> tags = Arrays.asList(Tag.of(OPERATION_NAME_TAG, operation));
meterRegistry.counter(QUERY_STATUS_COUNTER_METRIC_NAME + "." + status, tags).increment();
return super.instrumentExecutionResult(executionResult, parameters);
}
}
My application.yaml, just in case:
graphql:
servlet:
tracing-enabled: true
actuator-metrics: true
management:
endpoint:
metrics:
enabled: true
endpoints:
web:
exposure:
include: health,metrics
I'm using spring-boot-starter-parent:2.2.2.RELEASE, graphql-spring-boot-starter:6.0.0
I hope it helps.