Add instrumentation for graphql 20 that does not use deprecated methods (#10779)
This commit is contained in:
parent
9db9661bc9
commit
a3a572540e
|
@ -34,17 +34,25 @@ extra["testLatestDeps"] = testLatestDeps
|
||||||
abstract class TestLatestDepsRule : ComponentMetadataRule {
|
abstract class TestLatestDepsRule : ComponentMetadataRule {
|
||||||
override fun execute(context: ComponentMetadataContext) {
|
override fun execute(context: ComponentMetadataContext) {
|
||||||
val version = context.details.id.version
|
val version = context.details.id.version
|
||||||
if (version.contains("-alpha", true) ||
|
if (version.contains("-alpha", true)
|
||||||
version.contains("-beta", true) ||
|
|| version.contains("-beta", true)
|
||||||
version.contains("-rc", true) ||
|
|| version.contains("-rc", true)
|
||||||
version.contains("-m", true) || // e.g. spring milestones are published to grails repo
|
|| version.contains("-m", true) // e.g. spring milestones are published to grails repo
|
||||||
version.contains(".alpha", true) || // e.g. netty
|
|| version.contains(".alpha", true) // e.g. netty
|
||||||
version.contains(".beta", true) || // e.g. hibernate
|
|| version.contains(".beta", true) // e.g. hibernate
|
||||||
version.contains(".cr", true) // e.g. hibernate
|
|| version.contains(".cr", true) // e.g. hibernate
|
||||||
|
|| version.endsWith("-nf-execution") // graphql
|
||||||
|
|| GIT_SHA_PATTERN.matches(version) // graphql
|
||||||
|
|| DATETIME_PATTERN.matches(version) // graphql
|
||||||
) {
|
) {
|
||||||
context.details.status = "milestone"
|
context.details.status = "milestone"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val GIT_SHA_PATTERN = Regex("^.*-[0-9a-f]{7,}$")
|
||||||
|
private val DATETIME_PATTERN = Regex("^\\d{4}-\\d{2}-\\d{2}T\\d{2}-\\d{2}-\\d{2}.*$")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
|
|
|
@ -64,7 +64,7 @@ These are the supported libraries and frameworks:
|
||||||
| [Geode Client](https://geode.apache.org/) | 1.4+ | N/A | [Database Client Spans] |
|
| [Geode Client](https://geode.apache.org/) | 1.4+ | N/A | [Database Client Spans] |
|
||||||
| [Google HTTP Client](https://github.com/googleapis/google-http-java-client) | 1.19+ | N/A | [HTTP Client Spans], [HTTP Client Metrics] |
|
| [Google HTTP Client](https://github.com/googleapis/google-http-java-client) | 1.19+ | N/A | [HTTP Client Spans], [HTTP Client Metrics] |
|
||||||
| [Grails](https://grails.org/) | 3.0+ | N/A | Provides `http.route` [2], Controller Spans [3] |
|
| [Grails](https://grails.org/) | 3.0+ | N/A | Provides `http.route` [2], Controller Spans [3] |
|
||||||
| [GraphQL Java](https://www.graphql-java.com/) | 12.0+ | [opentelemetry-graphql-java-12.0](../instrumentation/graphql-java-12.0/library) | [GraphQL Server Spans] |
|
| [GraphQL Java](https://www.graphql-java.com/) | 12.0+ | [opentelemetry-graphql-java-12.0](../instrumentation/graphql-java/graphql-java-12.0/library),<br>[opentelemetry-graphql-java-20.0](../instrumentation/graphql-java/graphql-java-20.0/library) | [GraphQL Server Spans] |
|
||||||
| [gRPC](https://github.com/grpc/grpc-java) | 1.6+ | [opentelemetry-grpc-1.6](../instrumentation/grpc-1.6/library) | [RPC Client Spans], [RPC Client Metrics], [RPC Server Spans], [RPC Server Metrics] |
|
| [gRPC](https://github.com/grpc/grpc-java) | 1.6+ | [opentelemetry-grpc-1.6](../instrumentation/grpc-1.6/library) | [RPC Client Spans], [RPC Client Metrics], [RPC Server Spans], [RPC Server Metrics] |
|
||||||
| [Guava ListenableFuture](https://guava.dev/releases/snapshot/api/docs/com/google/common/util/concurrent/ListenableFuture.html) | 10.0+ | [opentelemetry-guava-10.0](../instrumentation/guava-10.0/library) | Context propagation |
|
| [Guava ListenableFuture](https://guava.dev/releases/snapshot/api/docs/com/google/common/util/concurrent/ListenableFuture.html) | 10.0+ | [opentelemetry-guava-10.0](../instrumentation/guava-10.0/library) | Context propagation |
|
||||||
| [GWT](http://www.gwtproject.org/) | 2.0+ | N/A | [RPC Server Spans] |
|
| [GWT](http://www.gwtproject.org/) | 2.0+ | N/A | [RPC Server Spans] |
|
||||||
|
@ -76,7 +76,7 @@ These are the supported libraries and frameworks:
|
||||||
| [Java Executors](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html) | Java 8+ | N/A | Context propagation |
|
| [Java Executors](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html) | Java 8+ | N/A | Context propagation |
|
||||||
| [Java Http Client](https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/package-summary.html) | Java 11+ | [opentelemetry-java-http-client](../instrumentation/java-http-client/library) | [HTTP Client Spans], [HTTP Client Metrics] |
|
| [Java Http Client](https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/package-summary.html) | Java 11+ | [opentelemetry-java-http-client](../instrumentation/java-http-client/library) | [HTTP Client Spans], [HTTP Client Metrics] |
|
||||||
| [java.util.logging](https://docs.oracle.com/javase/8/docs/api/java/util/logging/package-summary.html) | Java 8+ | N/A | none |
|
| [java.util.logging](https://docs.oracle.com/javase/8/docs/api/java/util/logging/package-summary.html) | Java 8+ | N/A | none |
|
||||||
| [Java Platform](https://docs.oracle.com/javase/8/docs/api/java/lang/management/ManagementFactory.html) | Java 8+ | [opentelemetry-runtime-telemetry-java8](../instrumentation/runtime-telemetry/runtime-telemetry-java8/library),[opentelemetry-runtime-telemetry-java17](../instrumentation/runtime-telemetry/runtime-telemetry-java17/library),<br>[opentelemetry-resources](../instrumentation/resources/library) | [JVM Runtime Metrics] |
|
| [Java Platform](https://docs.oracle.com/javase/8/docs/api/java/lang/management/ManagementFactory.html) | Java 8+ | [opentelemetry-runtime-telemetry-java8](../instrumentation/runtime-telemetry/runtime-telemetry-java8/library),<br>[opentelemetry-runtime-telemetry-java17](../instrumentation/runtime-telemetry/runtime-telemetry-java17/library),<br>[opentelemetry-resources](../instrumentation/resources/library) | [JVM Runtime Metrics] |
|
||||||
| [JAX-RS](https://javaee.github.io/javaee-spec/javadocs/javax/ws/rs/package-summary.html) | 0.5+ | N/A | Provides `http.route` [2], Controller Spans [3] |
|
| [JAX-RS](https://javaee.github.io/javaee-spec/javadocs/javax/ws/rs/package-summary.html) | 0.5+ | N/A | Provides `http.route` [2], Controller Spans [3] |
|
||||||
| [JAX-RS Client](https://javaee.github.io/javaee-spec/javadocs/javax/ws/rs/client/package-summary.html) | 1.1+ | N/A | [HTTP Client Spans], [HTTP Client Metrics] |
|
| [JAX-RS Client](https://javaee.github.io/javaee-spec/javadocs/javax/ws/rs/client/package-summary.html) | 1.1+ | N/A | [HTTP Client Spans], [HTTP Client Metrics] |
|
||||||
| [JAX-WS](https://jakarta.ee/specifications/xml-web-services/2.3/apidocs/javax/xml/ws/package-summary.html) | 2.0+ (not including 3.x yet) | N/A | Provides `http.route` [2], Controller Spans [3] |
|
| [JAX-WS](https://jakarta.ee/specifications/xml-web-services/2.3/apidocs/javax/xml/ws/package-summary.html) | 2.0+ (not including 3.x yet) | N/A | Provides `http.route` [2], Controller Spans [3] |
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
plugins {
|
|
||||||
id("otel.javaagent-instrumentation")
|
|
||||||
}
|
|
||||||
|
|
||||||
muzzle {
|
|
||||||
pass {
|
|
||||||
group.set("com.graphql-java")
|
|
||||||
module.set("graphql-java")
|
|
||||||
versions.set("[12,)")
|
|
||||||
skip("230521-nf-execution")
|
|
||||||
assertInverse.set(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(project(":instrumentation:graphql-java-12.0:library"))
|
|
||||||
|
|
||||||
library("com.graphql-java:graphql-java:12.0")
|
|
||||||
|
|
||||||
testImplementation(project(":instrumentation:graphql-java-12.0:testing"))
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.opentelemetry.instrumentation.graphql.v12_0;
|
|
||||||
|
|
||||||
import graphql.ExecutionResult;
|
|
||||||
import graphql.execution.instrumentation.Instrumentation;
|
|
||||||
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
|
|
||||||
import io.opentelemetry.api.OpenTelemetry;
|
|
||||||
import io.opentelemetry.api.trace.StatusCode;
|
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
|
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor;
|
|
||||||
|
|
||||||
@SuppressWarnings("AbbreviationAsWordInName")
|
|
||||||
public final class GraphQLTelemetry {
|
|
||||||
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.graphql-java-12.0";
|
|
||||||
|
|
||||||
/** Returns a new {@link GraphQLTelemetry} configured with the given {@link OpenTelemetry}. */
|
|
||||||
public static GraphQLTelemetry create(OpenTelemetry openTelemetry) {
|
|
||||||
return builder(openTelemetry).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new {@link GraphQLTelemetryBuilder} configured with the given {@link OpenTelemetry}.
|
|
||||||
*/
|
|
||||||
public static GraphQLTelemetryBuilder builder(OpenTelemetry openTelemetry) {
|
|
||||||
return new GraphQLTelemetryBuilder(openTelemetry);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Instrumenter<InstrumentationExecutionParameters, ExecutionResult> instrumenter;
|
|
||||||
private final boolean sanitizeQuery;
|
|
||||||
|
|
||||||
GraphQLTelemetry(OpenTelemetry openTelemetry, boolean sanitizeQuery) {
|
|
||||||
InstrumenterBuilder<InstrumentationExecutionParameters, ExecutionResult> builder =
|
|
||||||
Instrumenter.<InstrumentationExecutionParameters, ExecutionResult>builder(
|
|
||||||
openTelemetry, INSTRUMENTATION_NAME, ignored -> "GraphQL Operation")
|
|
||||||
.setSpanStatusExtractor(
|
|
||||||
(spanStatusBuilder, instrumentationExecutionParameters, executionResult, error) -> {
|
|
||||||
if (!executionResult.getErrors().isEmpty()) {
|
|
||||||
spanStatusBuilder.setStatus(StatusCode.ERROR);
|
|
||||||
} else {
|
|
||||||
SpanStatusExtractor.getDefault()
|
|
||||||
.extract(
|
|
||||||
spanStatusBuilder,
|
|
||||||
instrumentationExecutionParameters,
|
|
||||||
executionResult,
|
|
||||||
error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.addAttributesExtractor(new GraphqlAttributesExtractor());
|
|
||||||
|
|
||||||
this.instrumenter = builder.buildInstrumenter();
|
|
||||||
this.sanitizeQuery = sanitizeQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new {@link Instrumentation} that generates telemetry for received GraphQL requests.
|
|
||||||
*/
|
|
||||||
public Instrumentation newInstrumentation() {
|
|
||||||
return new OpenTelemetryInstrumentation(instrumenter, sanitizeQuery);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
plugins {
|
||||||
|
id("otel.javaagent-instrumentation")
|
||||||
|
}
|
||||||
|
|
||||||
|
muzzle {
|
||||||
|
pass {
|
||||||
|
group.set("com.graphql-java")
|
||||||
|
module.set("graphql-java")
|
||||||
|
versions.set("[12,20)")
|
||||||
|
skip("230521-nf-execution")
|
||||||
|
assertInverse.set(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":instrumentation:graphql-java:graphql-java-12.0:library"))
|
||||||
|
implementation(project(":instrumentation:graphql-java:graphql-java-common:library"))
|
||||||
|
|
||||||
|
library("com.graphql-java:graphql-java:12.0")
|
||||||
|
|
||||||
|
testInstrumentation(project(":instrumentation:graphql-java:graphql-java-20.0:javaagent"))
|
||||||
|
|
||||||
|
testImplementation(project(":instrumentation:graphql-java:graphql-java-common:testing"))
|
||||||
|
|
||||||
|
latestDepTestLibrary("com.graphql-java:graphql-java:19.+")
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ import net.bytebuddy.asm.Advice;
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
import net.bytebuddy.matcher.ElementMatcher;
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
public class GraphqlInstrumentation implements TypeInstrumentation {
|
class GraphqlInstrumentation implements TypeInstrumentation {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
|
@ -5,11 +5,15 @@
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.graphql.v12_0;
|
package io.opentelemetry.javaagent.instrumentation.graphql.v12_0;
|
||||||
|
|
||||||
|
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
|
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
|
||||||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@AutoService(InstrumentationModule.class)
|
@AutoService(InstrumentationModule.class)
|
||||||
|
@ -19,6 +23,13 @@ public class GraphqlInstrumentationModule extends InstrumentationModule {
|
||||||
super("graphql-java", "graphql-java-12.0");
|
super("graphql-java", "graphql-java-12.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
|
||||||
|
// added in 20.0
|
||||||
|
return not(
|
||||||
|
hasClassesNamed("graphql.execution.instrumentation.SimplePerformantInstrumentation"));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<TypeInstrumentation> typeInstrumentations() {
|
public List<TypeInstrumentation> typeInstrumentations() {
|
||||||
return Collections.singletonList(new GraphqlInstrumentation());
|
return Collections.singletonList(new GraphqlInstrumentation());
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.graphql.v12_0;
|
||||||
|
|
||||||
|
import graphql.execution.instrumentation.Instrumentation;
|
||||||
|
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||||
|
import io.opentelemetry.instrumentation.graphql.internal.InstrumentationUtil;
|
||||||
|
import io.opentelemetry.instrumentation.graphql.v12_0.GraphQLTelemetry;
|
||||||
|
import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig;
|
||||||
|
|
||||||
|
public final class GraphqlSingletons {
|
||||||
|
|
||||||
|
private static final boolean QUERY_SANITIZATION_ENABLED =
|
||||||
|
InstrumentationConfig.get()
|
||||||
|
.getBoolean("otel.instrumentation.graphql.query-sanitizer.enabled", true);
|
||||||
|
|
||||||
|
private static final GraphQLTelemetry TELEMETRY =
|
||||||
|
GraphQLTelemetry.builder(GlobalOpenTelemetry.get())
|
||||||
|
.setSanitizeQuery(QUERY_SANITIZATION_ENABLED)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
private GraphqlSingletons() {}
|
||||||
|
|
||||||
|
public static Instrumentation addInstrumentation(Instrumentation instrumentation) {
|
||||||
|
Instrumentation ourInstrumentation = TELEMETRY.newInstrumentation();
|
||||||
|
return InstrumentationUtil.addInstrumentation(instrumentation, ourInstrumentation);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
# Library Instrumentation for GraphQL Java version 12.0 and higher
|
# Library Instrumentation for GraphQL Java version 12.0 to 20.0
|
||||||
|
|
||||||
Provides OpenTelemetry instrumentation for [GraphQL Java](https://www.graphql-java.com/).
|
Provides OpenTelemetry instrumentation for [GraphQL Java](https://www.graphql-java.com/).
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
plugins {
|
||||||
|
id("otel.library-instrumentation")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
library("com.graphql-java:graphql-java:12.0")
|
||||||
|
implementation(project(":instrumentation:graphql-java:graphql-java-common:library"))
|
||||||
|
|
||||||
|
testImplementation(project(":instrumentation:graphql-java:graphql-java-common:testing"))
|
||||||
|
|
||||||
|
latestDepTestLibrary("com.graphql-java:graphql-java:19.+")
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.graphql.v12_0;
|
||||||
|
|
||||||
|
import graphql.execution.instrumentation.Instrumentation;
|
||||||
|
import io.opentelemetry.api.OpenTelemetry;
|
||||||
|
import io.opentelemetry.instrumentation.graphql.internal.OpenTelemetryInstrumentationHelper;
|
||||||
|
|
||||||
|
@SuppressWarnings("AbbreviationAsWordInName")
|
||||||
|
public final class GraphQLTelemetry {
|
||||||
|
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.graphql-java-12.0";
|
||||||
|
|
||||||
|
/** Returns a new {@link GraphQLTelemetry} configured with the given {@link OpenTelemetry}. */
|
||||||
|
public static GraphQLTelemetry create(OpenTelemetry openTelemetry) {
|
||||||
|
return builder(openTelemetry).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new {@link GraphQLTelemetryBuilder} configured with the given {@link OpenTelemetry}.
|
||||||
|
*/
|
||||||
|
public static GraphQLTelemetryBuilder builder(OpenTelemetry openTelemetry) {
|
||||||
|
return new GraphQLTelemetryBuilder(openTelemetry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final OpenTelemetryInstrumentationHelper helper;
|
||||||
|
|
||||||
|
GraphQLTelemetry(OpenTelemetry openTelemetry, boolean sanitizeQuery) {
|
||||||
|
helper =
|
||||||
|
OpenTelemetryInstrumentationHelper.create(
|
||||||
|
openTelemetry, INSTRUMENTATION_NAME, sanitizeQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new {@link Instrumentation} that generates telemetry for received GraphQL requests.
|
||||||
|
*/
|
||||||
|
public Instrumentation newInstrumentation() {
|
||||||
|
return new OpenTelemetryInstrumentation(helper);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.graphql.v12_0;
|
||||||
|
|
||||||
|
import graphql.ExecutionResult;
|
||||||
|
import graphql.execution.instrumentation.InstrumentationContext;
|
||||||
|
import graphql.execution.instrumentation.InstrumentationState;
|
||||||
|
import graphql.execution.instrumentation.SimpleInstrumentation;
|
||||||
|
import graphql.execution.instrumentation.parameters.InstrumentationExecuteOperationParameters;
|
||||||
|
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
|
||||||
|
import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters;
|
||||||
|
import graphql.schema.DataFetcher;
|
||||||
|
import io.opentelemetry.instrumentation.graphql.internal.OpenTelemetryInstrumentationHelper;
|
||||||
|
import io.opentelemetry.instrumentation.graphql.internal.OpenTelemetryInstrumentationState;
|
||||||
|
|
||||||
|
final class OpenTelemetryInstrumentation extends SimpleInstrumentation {
|
||||||
|
private final OpenTelemetryInstrumentationHelper helper;
|
||||||
|
|
||||||
|
OpenTelemetryInstrumentation(OpenTelemetryInstrumentationHelper helper) {
|
||||||
|
this.helper = helper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstrumentationState createState() {
|
||||||
|
return new OpenTelemetryInstrumentationState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstrumentationContext<ExecutionResult> beginExecution(
|
||||||
|
InstrumentationExecutionParameters parameters) {
|
||||||
|
OpenTelemetryInstrumentationState state = parameters.getInstrumentationState();
|
||||||
|
return helper.beginExecution(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstrumentationContext<ExecutionResult> beginExecuteOperation(
|
||||||
|
InstrumentationExecuteOperationParameters parameters) {
|
||||||
|
OpenTelemetryInstrumentationState state = parameters.getInstrumentationState();
|
||||||
|
return helper.beginExecuteOperation(parameters, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataFetcher<?> instrumentDataFetcher(
|
||||||
|
DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters) {
|
||||||
|
OpenTelemetryInstrumentationState state = parameters.getInstrumentationState();
|
||||||
|
return helper.instrumentDataFetcher(dataFetcher, state);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
plugins {
|
||||||
|
id("otel.javaagent-instrumentation")
|
||||||
|
}
|
||||||
|
|
||||||
|
muzzle {
|
||||||
|
pass {
|
||||||
|
group.set("com.graphql-java")
|
||||||
|
module.set("graphql-java")
|
||||||
|
versions.set("[20,)")
|
||||||
|
skip("230521-nf-execution")
|
||||||
|
assertInverse.set(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":instrumentation:graphql-java:graphql-java-20.0:library"))
|
||||||
|
implementation(project(":instrumentation:graphql-java:graphql-java-common:library"))
|
||||||
|
|
||||||
|
library("com.graphql-java:graphql-java:20.0")
|
||||||
|
|
||||||
|
testInstrumentation(project(":instrumentation:graphql-java:graphql-java-12.0:javaagent"))
|
||||||
|
|
||||||
|
testImplementation(project(":instrumentation:graphql-java:graphql-java-common:testing"))
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.graphql.v20_0;
|
||||||
|
|
||||||
|
import static io.opentelemetry.javaagent.instrumentation.graphql.v20_0.GraphqlSingletons.addInstrumentation;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||||
|
|
||||||
|
import graphql.execution.instrumentation.Instrumentation;
|
||||||
|
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
||||||
|
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
class GraphqlInstrumentation implements TypeInstrumentation {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return named("graphql.GraphQL");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transform(TypeTransformer transformer) {
|
||||||
|
transformer.applyAdviceToMethod(
|
||||||
|
namedOneOf("checkInstrumentationDefaultState", "checkInstrumentation")
|
||||||
|
.and(returns(named("graphql.execution.instrumentation.Instrumentation"))),
|
||||||
|
this.getClass().getName() + "$AddInstrumentationAdvice");
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public static class AddInstrumentationAdvice {
|
||||||
|
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||||
|
public static void onExit(@Advice.Return(readOnly = false) Instrumentation instrumentation) {
|
||||||
|
instrumentation = addInstrumentation(instrumentation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.graphql.v20_0;
|
||||||
|
|
||||||
|
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
|
||||||
|
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@AutoService(InstrumentationModule.class)
|
||||||
|
public class GraphqlInstrumentationModule extends InstrumentationModule {
|
||||||
|
|
||||||
|
public GraphqlInstrumentationModule() {
|
||||||
|
super("graphql-java", "graphql-java-20.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
|
||||||
|
// added in 20.0
|
||||||
|
return hasClassesNamed("graphql.execution.instrumentation.SimplePerformantInstrumentation");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<TypeInstrumentation> typeInstrumentations() {
|
||||||
|
return Collections.singletonList(new GraphqlInstrumentation());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.graphql.v20_0;
|
||||||
|
|
||||||
|
import graphql.execution.instrumentation.Instrumentation;
|
||||||
|
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||||
|
import io.opentelemetry.instrumentation.graphql.internal.InstrumentationUtil;
|
||||||
|
import io.opentelemetry.instrumentation.graphql.v20_0.GraphQLTelemetry;
|
||||||
|
import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig;
|
||||||
|
|
||||||
|
public final class GraphqlSingletons {
|
||||||
|
|
||||||
|
private static final boolean QUERY_SANITIZATION_ENABLED =
|
||||||
|
InstrumentationConfig.get()
|
||||||
|
.getBoolean("otel.instrumentation.graphql.query-sanitizer.enabled", true);
|
||||||
|
|
||||||
|
private static final GraphQLTelemetry TELEMETRY =
|
||||||
|
GraphQLTelemetry.builder(GlobalOpenTelemetry.get())
|
||||||
|
.setSanitizeQuery(QUERY_SANITIZATION_ENABLED)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
private GraphqlSingletons() {}
|
||||||
|
|
||||||
|
public static Instrumentation addInstrumentation(Instrumentation instrumentation) {
|
||||||
|
Instrumentation ourInstrumentation = TELEMETRY.newInstrumentation();
|
||||||
|
return InstrumentationUtil.addInstrumentation(instrumentation, ourInstrumentation);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.graphql.v20_0;
|
||||||
|
|
||||||
|
import graphql.GraphQL;
|
||||||
|
import io.opentelemetry.instrumentation.graphql.AbstractGraphqlTest;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
public class GraphqlTest extends AbstractGraphqlTest {
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
private static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected InstrumentationExtension getTesting() {
|
||||||
|
return testing;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(GraphQL.Builder builder) {}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Library Instrumentation for GraphQL Java version 20.0 and higher
|
||||||
|
|
||||||
|
Provides OpenTelemetry instrumentation for [GraphQL Java](https://www.graphql-java.com/).
|
||||||
|
|
||||||
|
## Quickstart
|
||||||
|
|
||||||
|
### Add these dependencies to your project
|
||||||
|
|
||||||
|
Replace `OPENTELEMETRY_VERSION` with the [latest
|
||||||
|
release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-graphql-java-12.0).
|
||||||
|
|
||||||
|
For Maven, add to your `pom.xml` dependencies:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.opentelemetry.instrumentation</groupId>
|
||||||
|
<artifactId>opentelemetry-graphql-java-20.0</artifactId>
|
||||||
|
<version>OPENTELEMETRY_VERSION</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
```
|
||||||
|
|
||||||
|
For Gradle, add to your dependencies:
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
implementation("io.opentelemetry.instrumentation:opentelemetry-graphql-java-20.0:OPENTELEMETRY_VERSION")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
The instrumentation library provides a GraphQL Java `Instrumentation` implementation that can be
|
||||||
|
added to an instance of the `GraphQL` to provide OpenTelemetry-based spans.
|
||||||
|
|
||||||
|
```java
|
||||||
|
void configure(OpenTelemetry openTelemetry, GraphQL.Builder builder) {
|
||||||
|
GraphQLTelemetry telemetry = GraphQLTelemetry.builder(openTelemetry).build();
|
||||||
|
builder.instrumentation(telemetry.newInstrumentation());
|
||||||
|
}
|
||||||
|
```
|
|
@ -0,0 +1,10 @@
|
||||||
|
plugins {
|
||||||
|
id("otel.library-instrumentation")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
library("com.graphql-java:graphql-java:20.0")
|
||||||
|
implementation(project(":instrumentation:graphql-java:graphql-java-common:library"))
|
||||||
|
|
||||||
|
testImplementation(project(":instrumentation:graphql-java:graphql-java-common:testing"))
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.graphql.v20_0;
|
||||||
|
|
||||||
|
import graphql.execution.instrumentation.Instrumentation;
|
||||||
|
import io.opentelemetry.api.OpenTelemetry;
|
||||||
|
import io.opentelemetry.instrumentation.graphql.internal.OpenTelemetryInstrumentationHelper;
|
||||||
|
|
||||||
|
@SuppressWarnings("AbbreviationAsWordInName")
|
||||||
|
public final class GraphQLTelemetry {
|
||||||
|
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.graphql-java-20.0";
|
||||||
|
|
||||||
|
/** Returns a new {@link GraphQLTelemetry} configured with the given {@link OpenTelemetry}. */
|
||||||
|
public static GraphQLTelemetry create(OpenTelemetry openTelemetry) {
|
||||||
|
return builder(openTelemetry).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new {@link GraphQLTelemetryBuilder} configured with the given {@link OpenTelemetry}.
|
||||||
|
*/
|
||||||
|
public static GraphQLTelemetryBuilder builder(OpenTelemetry openTelemetry) {
|
||||||
|
return new GraphQLTelemetryBuilder(openTelemetry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final OpenTelemetryInstrumentationHelper helper;
|
||||||
|
|
||||||
|
GraphQLTelemetry(OpenTelemetry openTelemetry, boolean sanitizeQuery) {
|
||||||
|
helper =
|
||||||
|
OpenTelemetryInstrumentationHelper.create(
|
||||||
|
openTelemetry, INSTRUMENTATION_NAME, sanitizeQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new {@link Instrumentation} that generates telemetry for received GraphQL requests.
|
||||||
|
*/
|
||||||
|
public Instrumentation newInstrumentation() {
|
||||||
|
return new OpenTelemetryInstrumentation(helper);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.graphql.v20_0;
|
||||||
|
|
||||||
|
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||||
|
import io.opentelemetry.api.OpenTelemetry;
|
||||||
|
|
||||||
|
/** A builder of {@link GraphQLTelemetry}. */
|
||||||
|
@SuppressWarnings("AbbreviationAsWordInName")
|
||||||
|
public final class GraphQLTelemetryBuilder {
|
||||||
|
|
||||||
|
private final OpenTelemetry openTelemetry;
|
||||||
|
|
||||||
|
private boolean sanitizeQuery = true;
|
||||||
|
|
||||||
|
GraphQLTelemetryBuilder(OpenTelemetry openTelemetry) {
|
||||||
|
this.openTelemetry = openTelemetry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets whether sensitive information should be removed from queries. Default is {@code true}. */
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public GraphQLTelemetryBuilder setSanitizeQuery(boolean sanitizeQuery) {
|
||||||
|
this.sanitizeQuery = sanitizeQuery;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new {@link GraphQLTelemetry} with the settings of this {@link
|
||||||
|
* GraphQLTelemetryBuilder}.
|
||||||
|
*/
|
||||||
|
public GraphQLTelemetry build() {
|
||||||
|
return new GraphQLTelemetry(openTelemetry, sanitizeQuery);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.graphql.v20_0;
|
||||||
|
|
||||||
|
import static graphql.execution.instrumentation.InstrumentationState.ofState;
|
||||||
|
|
||||||
|
import graphql.ExecutionResult;
|
||||||
|
import graphql.execution.instrumentation.InstrumentationContext;
|
||||||
|
import graphql.execution.instrumentation.InstrumentationState;
|
||||||
|
import graphql.execution.instrumentation.SimplePerformantInstrumentation;
|
||||||
|
import graphql.execution.instrumentation.parameters.InstrumentationCreateStateParameters;
|
||||||
|
import graphql.execution.instrumentation.parameters.InstrumentationExecuteOperationParameters;
|
||||||
|
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
|
||||||
|
import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters;
|
||||||
|
import graphql.schema.DataFetcher;
|
||||||
|
import io.opentelemetry.instrumentation.graphql.internal.OpenTelemetryInstrumentationHelper;
|
||||||
|
import io.opentelemetry.instrumentation.graphql.internal.OpenTelemetryInstrumentationState;
|
||||||
|
|
||||||
|
final class OpenTelemetryInstrumentation extends SimplePerformantInstrumentation {
|
||||||
|
private final OpenTelemetryInstrumentationHelper helper;
|
||||||
|
|
||||||
|
OpenTelemetryInstrumentation(OpenTelemetryInstrumentationHelper helper) {
|
||||||
|
this.helper = helper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstrumentationState createState(InstrumentationCreateStateParameters parameters) {
|
||||||
|
return new OpenTelemetryInstrumentationState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstrumentationContext<ExecutionResult> beginExecution(
|
||||||
|
InstrumentationExecutionParameters parameters, InstrumentationState rawState) {
|
||||||
|
OpenTelemetryInstrumentationState state = ofState(rawState);
|
||||||
|
return helper.beginExecution(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstrumentationContext<ExecutionResult> beginExecuteOperation(
|
||||||
|
InstrumentationExecuteOperationParameters parameters, InstrumentationState rawState) {
|
||||||
|
OpenTelemetryInstrumentationState state = ofState(rawState);
|
||||||
|
return helper.beginExecuteOperation(parameters, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataFetcher<?> instrumentDataFetcher(
|
||||||
|
DataFetcher<?> dataFetcher,
|
||||||
|
InstrumentationFieldFetchParameters parameters,
|
||||||
|
InstrumentationState rawState) {
|
||||||
|
OpenTelemetryInstrumentationState state = ofState(rawState);
|
||||||
|
return helper.instrumentDataFetcher(dataFetcher, state);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.graphql.v20_0;
|
||||||
|
|
||||||
|
import graphql.GraphQL;
|
||||||
|
import io.opentelemetry.instrumentation.graphql.AbstractGraphqlTest;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
public class GraphqlTest extends AbstractGraphqlTest {
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
private static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected InstrumentationExtension getTesting() {
|
||||||
|
return testing;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(GraphQL.Builder builder) {
|
||||||
|
GraphQLTelemetry telemetry = GraphQLTelemetry.builder(testing.getOpenTelemetry()).build();
|
||||||
|
builder.instrumentation(telemetry.newInstrumentation());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
plugins {
|
||||||
|
id("otel.javaagent-instrumentation")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":instrumentation:graphql-java:graphql-java-common:library"))
|
||||||
|
|
||||||
|
library("com.graphql-java:graphql-java:12.0")
|
||||||
|
}
|
|
@ -4,6 +4,4 @@ plugins {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
library("com.graphql-java:graphql-java:12.0")
|
library("com.graphql-java:graphql-java:12.0")
|
||||||
|
|
||||||
testImplementation(project(":instrumentation:graphql-java-12.0:testing"))
|
|
||||||
}
|
}
|
|
@ -3,10 +3,9 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.opentelemetry.instrumentation.graphql.v12_0;
|
package io.opentelemetry.instrumentation.graphql.internal;
|
||||||
|
|
||||||
import graphql.ExecutionResult;
|
import graphql.ExecutionResult;
|
||||||
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
|
|
||||||
import io.opentelemetry.api.common.AttributeKey;
|
import io.opentelemetry.api.common.AttributeKey;
|
||||||
import io.opentelemetry.api.common.AttributesBuilder;
|
import io.opentelemetry.api.common.AttributesBuilder;
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
|
@ -14,8 +13,12 @@ import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
|
||||||
|
* any time.
|
||||||
|
*/
|
||||||
final class GraphqlAttributesExtractor
|
final class GraphqlAttributesExtractor
|
||||||
implements AttributesExtractor<InstrumentationExecutionParameters, ExecutionResult> {
|
implements AttributesExtractor<OpenTelemetryInstrumentationState, ExecutionResult> {
|
||||||
// https://github.com/open-telemetry/semantic-conventions/blob/main/docs/database/graphql.md
|
// https://github.com/open-telemetry/semantic-conventions/blob/main/docs/database/graphql.md
|
||||||
private static final AttributeKey<String> OPERATION_NAME =
|
private static final AttributeKey<String> OPERATION_NAME =
|
||||||
AttributeKey.stringKey("graphql.operation.name");
|
AttributeKey.stringKey("graphql.operation.name");
|
||||||
|
@ -28,20 +31,19 @@ final class GraphqlAttributesExtractor
|
||||||
public void onStart(
|
public void onStart(
|
||||||
AttributesBuilder attributes,
|
AttributesBuilder attributes,
|
||||||
Context parentContext,
|
Context parentContext,
|
||||||
InstrumentationExecutionParameters request) {}
|
OpenTelemetryInstrumentationState request) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnd(
|
public void onEnd(
|
||||||
AttributesBuilder attributes,
|
AttributesBuilder attributes,
|
||||||
Context context,
|
Context context,
|
||||||
InstrumentationExecutionParameters request,
|
OpenTelemetryInstrumentationState request,
|
||||||
@Nullable ExecutionResult response,
|
@Nullable ExecutionResult response,
|
||||||
@Nullable Throwable error) {
|
@Nullable Throwable error) {
|
||||||
OpenTelemetryInstrumentationState state = request.getInstrumentationState();
|
attributes.put(OPERATION_NAME, request.getOperationName());
|
||||||
attributes.put(OPERATION_NAME, state.getOperationName());
|
if (request.getOperation() != null) {
|
||||||
if (state.getOperation() != null) {
|
attributes.put(OPERATION_TYPE, request.getOperation().name().toLowerCase(Locale.ROOT));
|
||||||
attributes.put(OPERATION_TYPE, state.getOperation().name().toLowerCase(Locale.ROOT));
|
|
||||||
}
|
}
|
||||||
attributes.put(GRAPHQL_DOCUMENT, state.getQuery());
|
attributes.put(GRAPHQL_DOCUMENT, request.getQuery());
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,31 +3,21 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.opentelemetry.javaagent.instrumentation.graphql.v12_0;
|
package io.opentelemetry.instrumentation.graphql.internal;
|
||||||
|
|
||||||
import graphql.execution.instrumentation.ChainedInstrumentation;
|
import graphql.execution.instrumentation.ChainedInstrumentation;
|
||||||
import graphql.execution.instrumentation.Instrumentation;
|
import graphql.execution.instrumentation.Instrumentation;
|
||||||
import io.opentelemetry.api.GlobalOpenTelemetry;
|
|
||||||
import io.opentelemetry.instrumentation.graphql.v12_0.GraphQLTelemetry;
|
|
||||||
import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public final class GraphqlSingletons {
|
/**
|
||||||
|
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
|
||||||
|
* any time.
|
||||||
|
*/
|
||||||
|
public final class InstrumentationUtil {
|
||||||
|
|
||||||
private static final boolean QUERY_SANITIZATION_ENABLED =
|
public static Instrumentation addInstrumentation(
|
||||||
InstrumentationConfig.get()
|
Instrumentation instrumentation, Instrumentation ourInstrumentation) {
|
||||||
.getBoolean("otel.instrumentation.graphql.query-sanitizer.enabled", true);
|
|
||||||
|
|
||||||
private static final GraphQLTelemetry TELEMETRY =
|
|
||||||
GraphQLTelemetry.builder(GlobalOpenTelemetry.get())
|
|
||||||
.setSanitizeQuery(QUERY_SANITIZATION_ENABLED)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private GraphqlSingletons() {}
|
|
||||||
|
|
||||||
public static Instrumentation addInstrumentation(Instrumentation instrumentation) {
|
|
||||||
Instrumentation ourInstrumentation = TELEMETRY.newInstrumentation();
|
|
||||||
if (instrumentation == null) {
|
if (instrumentation == null) {
|
||||||
return ourInstrumentation;
|
return ourInstrumentation;
|
||||||
}
|
}
|
||||||
|
@ -47,4 +37,6 @@ public final class GraphqlSingletons {
|
||||||
}
|
}
|
||||||
return new ChainedInstrumentation(instrumentationList);
|
return new ChainedInstrumentation(instrumentationList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private InstrumentationUtil() {}
|
||||||
}
|
}
|
|
@ -3,17 +3,13 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.opentelemetry.instrumentation.graphql.v12_0;
|
package io.opentelemetry.instrumentation.graphql.internal;
|
||||||
|
|
||||||
import graphql.ExecutionResult;
|
import graphql.ExecutionResult;
|
||||||
import graphql.GraphQLError;
|
import graphql.GraphQLError;
|
||||||
import graphql.execution.instrumentation.InstrumentationContext;
|
import graphql.execution.instrumentation.InstrumentationContext;
|
||||||
import graphql.execution.instrumentation.InstrumentationState;
|
|
||||||
import graphql.execution.instrumentation.SimpleInstrumentation;
|
|
||||||
import graphql.execution.instrumentation.SimpleInstrumentationContext;
|
import graphql.execution.instrumentation.SimpleInstrumentationContext;
|
||||||
import graphql.execution.instrumentation.parameters.InstrumentationExecuteOperationParameters;
|
import graphql.execution.instrumentation.parameters.InstrumentationExecuteOperationParameters;
|
||||||
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
|
|
||||||
import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters;
|
|
||||||
import graphql.language.AstPrinter;
|
import graphql.language.AstPrinter;
|
||||||
import graphql.language.AstTransformer;
|
import graphql.language.AstTransformer;
|
||||||
import graphql.language.BooleanValue;
|
import graphql.language.BooleanValue;
|
||||||
|
@ -23,52 +19,75 @@ import graphql.language.NodeVisitor;
|
||||||
import graphql.language.NodeVisitorStub;
|
import graphql.language.NodeVisitorStub;
|
||||||
import graphql.language.NullValue;
|
import graphql.language.NullValue;
|
||||||
import graphql.language.OperationDefinition;
|
import graphql.language.OperationDefinition;
|
||||||
import graphql.language.OperationDefinition.Operation;
|
|
||||||
import graphql.language.Value;
|
import graphql.language.Value;
|
||||||
import graphql.language.VariableReference;
|
import graphql.language.VariableReference;
|
||||||
import graphql.schema.DataFetcher;
|
import graphql.schema.DataFetcher;
|
||||||
import graphql.util.TraversalControl;
|
import graphql.util.TraversalControl;
|
||||||
import graphql.util.TraverserContext;
|
import graphql.util.TraverserContext;
|
||||||
import graphql.util.TreeTransformerUtil;
|
import graphql.util.TreeTransformerUtil;
|
||||||
|
import io.opentelemetry.api.OpenTelemetry;
|
||||||
import io.opentelemetry.api.common.Attributes;
|
import io.opentelemetry.api.common.Attributes;
|
||||||
import io.opentelemetry.api.common.AttributesBuilder;
|
import io.opentelemetry.api.common.AttributesBuilder;
|
||||||
import io.opentelemetry.api.trace.Span;
|
import io.opentelemetry.api.trace.Span;
|
||||||
|
import io.opentelemetry.api.trace.StatusCode;
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.context.Scope;
|
import io.opentelemetry.context.Scope;
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor;
|
||||||
import io.opentelemetry.semconv.SemanticAttributes;
|
import io.opentelemetry.semconv.SemanticAttributes;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
final class OpenTelemetryInstrumentation extends SimpleInstrumentation {
|
/**
|
||||||
|
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
|
||||||
|
* any time.
|
||||||
|
*/
|
||||||
|
public final class OpenTelemetryInstrumentationHelper {
|
||||||
private static final NodeVisitor sanitizingVisitor = new SanitizingVisitor();
|
private static final NodeVisitor sanitizingVisitor = new SanitizingVisitor();
|
||||||
private static final AstTransformer astTransformer = new AstTransformer();
|
private static final AstTransformer astTransformer = new AstTransformer();
|
||||||
|
|
||||||
private final Instrumenter<InstrumentationExecutionParameters, ExecutionResult> instrumenter;
|
private final Instrumenter<OpenTelemetryInstrumentationState, ExecutionResult> instrumenter;
|
||||||
private final boolean sanitizeQuery;
|
private final boolean sanitizeQuery;
|
||||||
|
|
||||||
OpenTelemetryInstrumentation(
|
private OpenTelemetryInstrumentationHelper(
|
||||||
Instrumenter<InstrumentationExecutionParameters, ExecutionResult> instrumenter,
|
Instrumenter<OpenTelemetryInstrumentationState, ExecutionResult> instrumenter,
|
||||||
boolean sanitizeQuery) {
|
boolean sanitizeQuery) {
|
||||||
this.instrumenter = instrumenter;
|
this.instrumenter = instrumenter;
|
||||||
this.sanitizeQuery = sanitizeQuery;
|
this.sanitizeQuery = sanitizeQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static OpenTelemetryInstrumentationHelper create(
|
||||||
public InstrumentationState createState() {
|
OpenTelemetry openTelemetry, String instrumentationName, boolean sanitizeQuery) {
|
||||||
return new OpenTelemetryInstrumentationState();
|
InstrumenterBuilder<OpenTelemetryInstrumentationState, ExecutionResult> builder =
|
||||||
|
Instrumenter.<OpenTelemetryInstrumentationState, ExecutionResult>builder(
|
||||||
|
openTelemetry, instrumentationName, ignored -> "GraphQL Operation")
|
||||||
|
.setSpanStatusExtractor(
|
||||||
|
(spanStatusBuilder, instrumentationExecutionParameters, executionResult, error) -> {
|
||||||
|
if (!executionResult.getErrors().isEmpty()) {
|
||||||
|
spanStatusBuilder.setStatus(StatusCode.ERROR);
|
||||||
|
} else {
|
||||||
|
SpanStatusExtractor.getDefault()
|
||||||
|
.extract(
|
||||||
|
spanStatusBuilder,
|
||||||
|
instrumentationExecutionParameters,
|
||||||
|
executionResult,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.addAttributesExtractor(new GraphqlAttributesExtractor());
|
||||||
|
|
||||||
|
return new OpenTelemetryInstrumentationHelper(builder.buildInstrumenter(), sanitizeQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public InstrumentationContext<ExecutionResult> beginExecution(
|
public InstrumentationContext<ExecutionResult> beginExecution(
|
||||||
InstrumentationExecutionParameters parameters) {
|
OpenTelemetryInstrumentationState state) {
|
||||||
|
|
||||||
Context parentContext = Context.current();
|
Context parentContext = Context.current();
|
||||||
if (!instrumenter.shouldStart(parentContext, parameters)) {
|
if (!instrumenter.shouldStart(parentContext, state)) {
|
||||||
return SimpleInstrumentationContext.noOp();
|
return SimpleInstrumentationContext.noOp();
|
||||||
}
|
}
|
||||||
|
|
||||||
Context context = instrumenter.start(parentContext, parameters);
|
Context context = instrumenter.start(parentContext, state);
|
||||||
OpenTelemetryInstrumentationState state = parameters.getInstrumentationState();
|
|
||||||
state.setContext(context);
|
state.setContext(context);
|
||||||
|
|
||||||
return SimpleInstrumentationContext.whenCompleted(
|
return SimpleInstrumentationContext.whenCompleted(
|
||||||
|
@ -82,20 +101,18 @@ final class OpenTelemetryInstrumentation extends SimpleInstrumentation {
|
||||||
span.addEvent(SemanticAttributes.EXCEPTION_EVENT_NAME, attributes.build());
|
span.addEvent(SemanticAttributes.EXCEPTION_EVENT_NAME, attributes.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
instrumenter.end(context, parameters, result, throwable);
|
instrumenter.end(context, state, result, throwable);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public InstrumentationContext<ExecutionResult> beginExecuteOperation(
|
public InstrumentationContext<ExecutionResult> beginExecuteOperation(
|
||||||
InstrumentationExecuteOperationParameters parameters) {
|
InstrumentationExecuteOperationParameters parameters,
|
||||||
|
OpenTelemetryInstrumentationState state) {
|
||||||
OpenTelemetryInstrumentationState state = parameters.getInstrumentationState();
|
|
||||||
Span span = Span.fromContext(state.getContext());
|
Span span = Span.fromContext(state.getContext());
|
||||||
|
|
||||||
OperationDefinition operationDefinition =
|
OperationDefinition operationDefinition =
|
||||||
parameters.getExecutionContext().getOperationDefinition();
|
parameters.getExecutionContext().getOperationDefinition();
|
||||||
Operation operation = operationDefinition.getOperation();
|
OperationDefinition.Operation operation = operationDefinition.getOperation();
|
||||||
String operationType = operation.name().toLowerCase(Locale.ROOT);
|
String operationType = operation.name().toLowerCase(Locale.ROOT);
|
||||||
String operationName = operationDefinition.getName();
|
String operationName = operationDefinition.getName();
|
||||||
|
|
||||||
|
@ -117,10 +134,8 @@ final class OpenTelemetryInstrumentation extends SimpleInstrumentation {
|
||||||
return SimpleInstrumentationContext.noOp();
|
return SimpleInstrumentationContext.noOp();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public DataFetcher<?> instrumentDataFetcher(
|
public DataFetcher<?> instrumentDataFetcher(
|
||||||
DataFetcher<?> dataFetcher, InstrumentationFieldFetchParameters parameters) {
|
DataFetcher<?> dataFetcher, OpenTelemetryInstrumentationState state) {
|
||||||
OpenTelemetryInstrumentationState state = parameters.getInstrumentationState();
|
|
||||||
Context context = state.getContext();
|
Context context = state.getContext();
|
||||||
|
|
||||||
return (DataFetcher<Object>)
|
return (DataFetcher<Object>)
|
|
@ -3,13 +3,17 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.opentelemetry.instrumentation.graphql.v12_0;
|
package io.opentelemetry.instrumentation.graphql.internal;
|
||||||
|
|
||||||
import graphql.execution.instrumentation.InstrumentationState;
|
import graphql.execution.instrumentation.InstrumentationState;
|
||||||
import graphql.language.OperationDefinition.Operation;
|
import graphql.language.OperationDefinition.Operation;
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
|
|
||||||
final class OpenTelemetryInstrumentationState implements InstrumentationState {
|
/**
|
||||||
|
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
|
||||||
|
* any time.
|
||||||
|
*/
|
||||||
|
public final class OpenTelemetryInstrumentationState implements InstrumentationState {
|
||||||
private Context context;
|
private Context context;
|
||||||
private Operation operation;
|
private Operation operation;
|
||||||
private String operationName;
|
private String operationName;
|
|
@ -180,7 +180,7 @@ public abstract class AbstractGraphqlTest {
|
||||||
equalTo(AttributeKey.stringKey("graphql.operation.type"), "query"),
|
equalTo(AttributeKey.stringKey("graphql.operation.type"), "query"),
|
||||||
normalizedQueryEqualsTo(
|
normalizedQueryEqualsTo(
|
||||||
AttributeKey.stringKey("graphql.document"),
|
AttributeKey.stringKey("graphql.document"),
|
||||||
"query { bookById(id: ?) { name } }")),
|
"{ bookById(id: ?) { name } }")),
|
||||||
span -> span.hasName("fetchBookById").hasParent(trace.getSpan(0))));
|
span -> span.hasName("fetchBookById").hasParent(trace.getSpan(0))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,7 +211,8 @@ public abstract class AbstractGraphqlTest {
|
||||||
satisfies(
|
satisfies(
|
||||||
SemanticAttributes.EXCEPTION_MESSAGE,
|
SemanticAttributes.EXCEPTION_MESSAGE,
|
||||||
message ->
|
message ->
|
||||||
message.startsWith("Invalid Syntax"))))));
|
message.startsWithIgnoringCase(
|
||||||
|
"Invalid Syntax"))))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -252,8 +253,7 @@ public abstract class AbstractGraphqlTest {
|
||||||
satisfies(
|
satisfies(
|
||||||
SemanticAttributes.EXCEPTION_MESSAGE,
|
SemanticAttributes.EXCEPTION_MESSAGE,
|
||||||
message ->
|
message ->
|
||||||
message.startsWith(
|
message.startsWith("Validation error"))))));
|
||||||
"Validation error of type FieldUndefined"))))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -295,6 +295,9 @@ public abstract class AbstractGraphqlTest {
|
||||||
stringAssert.satisfies(
|
stringAssert.satisfies(
|
||||||
querySource -> {
|
querySource -> {
|
||||||
String normalized = querySource.replaceAll("(?s)\\s+", " ");
|
String normalized = querySource.replaceAll("(?s)\\s+", " ");
|
||||||
|
if (normalized.startsWith("query {")) {
|
||||||
|
normalized = normalized.substring("query ".length());
|
||||||
|
}
|
||||||
assertThat(normalized).isEqualTo(value);
|
assertThat(normalized).isEqualTo(value);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
|
@ -191,9 +191,12 @@ include(":instrumentation:cassandra:cassandra-4.4:library")
|
||||||
include(":instrumentation:cassandra:cassandra-4.4:testing")
|
include(":instrumentation:cassandra:cassandra-4.4:testing")
|
||||||
include(":instrumentation:cassandra:cassandra-4-common:testing")
|
include(":instrumentation:cassandra:cassandra-4-common:testing")
|
||||||
include(":instrumentation:cdi-testing")
|
include(":instrumentation:cdi-testing")
|
||||||
include(":instrumentation:graphql-java-12.0:javaagent")
|
include(":instrumentation:graphql-java:graphql-java-12.0:javaagent")
|
||||||
include(":instrumentation:graphql-java-12.0:library")
|
include(":instrumentation:graphql-java:graphql-java-12.0:library")
|
||||||
include(":instrumentation:graphql-java-12.0:testing")
|
include(":instrumentation:graphql-java:graphql-java-20.0:javaagent")
|
||||||
|
include(":instrumentation:graphql-java:graphql-java-20.0:library")
|
||||||
|
include(":instrumentation:graphql-java:graphql-java-common:library")
|
||||||
|
include(":instrumentation:graphql-java:graphql-java-common:testing")
|
||||||
include(":instrumentation:internal:internal-application-logger:bootstrap")
|
include(":instrumentation:internal:internal-application-logger:bootstrap")
|
||||||
include(":instrumentation:internal:internal-application-logger:javaagent")
|
include(":instrumentation:internal:internal-application-logger:javaagent")
|
||||||
include(":instrumentation:internal:internal-class-loader:javaagent")
|
include(":instrumentation:internal:internal-class-loader:javaagent")
|
||||||
|
|
Loading…
Reference in New Issue