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:
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user