Add distribution summary metric for datafetcher calls

This commit adds a new `"graphql.request.datafetch.count"` distribution
summary metric for counting the number of data fetching calls per
request.

Closes gh-146
This commit is contained in:
Brian Clozel
2021-09-22 16:53:08 +02:00
parent 74ba9061d5
commit 552c4f72d0
2 changed files with 36 additions and 2 deletions

View File

@@ -17,6 +17,7 @@
package org.springframework.graphql.boot.actuate.metrics;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.atomic.AtomicLong;
import graphql.ExecutionResult;
import graphql.execution.instrumentation.InstrumentationContext;
@@ -26,6 +27,7 @@ import graphql.execution.instrumentation.SimpleInstrumentationContext;
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters;
import graphql.schema.DataFetcher;
import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
@@ -41,10 +43,16 @@ class GraphQlMetricsInstrumentation extends SimpleInstrumentation {
private final AutoTimer autoTimer;
private final DistributionSummary dataFetchingSummary;
GraphQlMetricsInstrumentation(MeterRegistry registry, GraphQlTagsProvider tagsProvider, AutoTimer autoTimer) {
this.registry = registry;
this.tagsProvider = tagsProvider;
this.autoTimer = autoTimer;
this.dataFetchingSummary = DistributionSummary.builder("graphql.request.datafetch.count")
.baseUnit("calls")
.description("Count of DataFetcher calls per request.")
.register(this.registry);
}
@Override
@@ -66,9 +74,10 @@ class GraphQlMetricsInstrumentation extends SimpleInstrumentation {
if (!result.getErrors().isEmpty()) {
result.getErrors()
.forEach((error) -> GraphQlMetricsInstrumentation.this.registry.counter("graphql.error",
GraphQlMetricsInstrumentation.this.tagsProvider.getErrorTags(parameters, error))
GraphQlMetricsInstrumentation.this.tagsProvider.getErrorTags(parameters, error))
.increment());
}
GraphQlMetricsInstrumentation.this.dataFetchingSummary.record(state.getDataFetchingCount());
}
};
}
@@ -92,7 +101,6 @@ class GraphQlMetricsInstrumentation extends SimpleInstrumentation {
recordDataFetcherMetric(sample, dataFetcher, parameters, null);
return value;
}
}
catch (Throwable throwable) {
recordDataFetcherMetric(sample, dataFetcher, parameters, throwable);
@@ -108,6 +116,8 @@ class GraphQlMetricsInstrumentation extends SimpleInstrumentation {
Timer.Builder timer = this.autoTimer.builder("graphql.datafetcher");
timer.tags(this.tagsProvider.getDataFetchingTags(dataFetcher, parameters, throwable));
sample.stop(timer.register(this.registry));
RequestMetricsInstrumentationState state = parameters.getInstrumentationState();
state.incrementDataFetchingCount();
}
static class RequestMetricsInstrumentationState implements InstrumentationState {
@@ -118,6 +128,8 @@ class GraphQlMetricsInstrumentation extends SimpleInstrumentation {
private Timer.Sample sample;
private AtomicLong dataFetchingCount = new AtomicLong(0L);
RequestMetricsInstrumentationState(AutoTimer autoTimer, MeterRegistry registry) {
this.timer = autoTimer.builder("graphql.request");
this.registry = registry;
@@ -136,6 +148,14 @@ class GraphQlMetricsInstrumentation extends SimpleInstrumentation {
this.sample.stop(this.timer.register(this.registry));
}
void incrementDataFetchingCount() {
this.dataFetchingCount.incrementAndGet();
}
long getDataFetchingCount() {
return this.dataFetchingCount.get();
}
}
}

View File

@@ -309,6 +309,20 @@ A `DataFetcher` metric timer is available at `/actuator/metrics/graphql.datafetc
|"SUCCESS", "ERROR"
|===
[[boot-graphql-metrics-datafetcher-summary]]
=== `DataFetcher` Distribution Summary
A https://micrometer.io/docs/concepts#_distribution_summaries[distribution summary]
that counts the number of non-trivial `DataFetcher` calls made per request.
This metric is useful for detecting "N+1" data fetching issues and consider batch loading;
it provides the `"TOTAL"` number of data fetcher calls made over the `"COUNT"` of recorded requests,
as well as the `"MAX"` calls made for a single request over the considered period.
The distribution is available at `/actuator/metrics/graphql.request.datafetch.count`.
More options are available for
{spring-boot-ref-docs}/application-properties.html#application-properties.actuator.management.metrics.distribution.maximum-expected-value[configuring distributions with application properties].
[[boot-graphql-metrics-error-counter]]
=== Error Counter