org.opentripplanner.ext.actuator.MicrometerGraphQLInstrumentation Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of otp Show documentation
Show all versions of otp Show documentation
The OpenTripPlanner multimodal journey planning system
package org.opentripplanner.ext.actuator;
import static graphql.execution.instrumentation.SimpleInstrumentationContext.noOp;
import static graphql.execution.instrumentation.SimpleInstrumentationContext.whenCompleted;
import graphql.ExecutionResult;
import graphql.execution.instrumentation.ExecutionStrategyInstrumentationContext;
import graphql.execution.instrumentation.Instrumentation;
import graphql.execution.instrumentation.InstrumentationContext;
import graphql.execution.instrumentation.InstrumentationState;
import graphql.execution.instrumentation.parameters.InstrumentationCreateStateParameters;
import graphql.execution.instrumentation.parameters.InstrumentationExecuteOperationParameters;
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
import graphql.execution.instrumentation.parameters.InstrumentationExecutionStrategyParameters;
import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters;
import graphql.execution.instrumentation.parameters.InstrumentationFieldParameters;
import graphql.execution.instrumentation.parameters.InstrumentationValidationParameters;
import graphql.language.Document;
import graphql.schema.GraphQLTypeUtil;
import graphql.validation.ValidationError;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Nonnull;
/**
* Using this instrumentation we can precisely measure how queries and data fetchers are executed
* and export the metrics to [micrometer](https://micrometer.io).
*
* There are two types of metrics: one for query execution, and another for resolver timing. The
* timers are registered to micrometer using graphql.timer.query and graphql.timer.resolver.
*
* ### See also:
* - https://github.com/symbaloo/graphql-micrometer/blob/main/src/main/kotlin/com/symbaloo/graphqlmicrometer/MicrometerInstrumentation.kt
* - https://github.com/graphql-java-kickstart/graphql-spring-boot/blob/master/graphql-spring-boot-autoconfigure/src/main/java/graphql/kickstart/autoconfigure/web/servlet/metrics/MetricsInstrumentation.java
* - https://github.com/apollographql/apollo-tracing - [TracingInstrumentation]
*/
public class MicrometerGraphQLInstrumentation implements Instrumentation {
private static final String QUERY_TIME_METRIC_NAME = "graphql.timer.query";
private static final String RESOLVER_TIME_METRIC_NAME = "graphql.timer.resolver";
private static final String OPERATION_NAME_TAG = "operationName";
private static final String OPERATION = "operation";
private static final String PARENT = "parent";
private static final String FIELD = "field";
private static final String TIMER_DESCRIPTION =
"Timer that records the time to fetch the data by Operation Name";
private final MeterRegistry meterRegistry;
private Iterable tags;
public MicrometerGraphQLInstrumentation(MeterRegistry meterRegistry, Iterable tags) {
this.meterRegistry = meterRegistry;
this.tags = tags;
}
@Override
public InstrumentationState createState(InstrumentationCreateStateParameters parameters) {
return new TraceState(parameters.getExecutionInput().getOperationName());
}
@Override
public InstrumentationContext beginExecution(
InstrumentationExecutionParameters parameters
) {
TraceState state = parameters.getInstrumentationState();
Timer.Sample sample = Timer.start(meterRegistry);
return whenCompleted((res, err) -> sample.stop(
buildQueryTimer(state.operationName, "execution")));
}
@Override
public InstrumentationContext beginParse(InstrumentationExecutionParameters parameters) {
TraceState state = parameters.getInstrumentationState();
Timer.Sample sample = Timer.start(meterRegistry);
return whenCompleted((res, err) -> sample.stop(
buildQueryTimer(state.operationName, "parse")));
}
@Override
public InstrumentationContext> beginValidation(
InstrumentationValidationParameters parameters
) {
TraceState state = parameters.getInstrumentationState();
Timer.Sample sample = Timer.start(meterRegistry);
return whenCompleted((res, err) -> sample.stop(
buildQueryTimer(state.operationName, "validation")));
}
@Override
public InstrumentationContext beginExecuteOperation(
InstrumentationExecuteOperationParameters parameters
) {
return noOp();
}
@Override
public ExecutionStrategyInstrumentationContext beginExecutionStrategy(
InstrumentationExecutionStrategyParameters parameters
) {
return new ExecutionStrategyInstrumentationContext() {
@Override
public void onDispatched(CompletableFuture result) {}
@Override
public void onCompleted(ExecutionResult result, Throwable t) {}
};
}
@Override
public InstrumentationContext beginField(InstrumentationFieldParameters parameters) {
return noOp();
}
@Override
public InstrumentationContext