diff --git a/conventions/src/main/kotlin/io.opentelemetry.instrumentation.base.gradle.kts b/conventions/src/main/kotlin/io.opentelemetry.instrumentation.base.gradle.kts index 01300ea7344f..95c3309afedd 100644 --- a/conventions/src/main/kotlin/io.opentelemetry.instrumentation.base.gradle.kts +++ b/conventions/src/main/kotlin/io.opentelemetry.instrumentation.base.gradle.kts @@ -34,17 +34,25 @@ extra["testLatestDeps"] = testLatestDeps abstract class TestLatestDepsRule : ComponentMetadataRule { override fun execute(context: ComponentMetadataContext) { val version = context.details.id.version - if (version.contains("-alpha", true) || - version.contains("-beta", true) || - version.contains("-rc", true) || - version.contains("-m", true) || // e.g. spring milestones are published to grails repo - version.contains(".alpha", true) || // e.g. netty - version.contains(".beta", true) || // e.g. hibernate - version.contains(".cr", true) // e.g. hibernate + if (version.contains("-alpha", true) + || version.contains("-beta", true) + || version.contains("-rc", true) + || version.contains("-m", true) // e.g. spring milestones are published to grails repo + || version.contains(".alpha", true) // e.g. netty + || version.contains(".beta", 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" } } + + 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 { diff --git a/docs/supported-libraries.md b/docs/supported-libraries.md index cec90fdcb4a2..7a843adf91cf 100644 --- a/docs/supported-libraries.md +++ b/docs/supported-libraries.md @@ -60,7 +60,7 @@ These are the supported libraries and frameworks: | [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] | | [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),
[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] | | [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] | @@ -72,7 +72,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 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 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),
[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),
[opentelemetry-runtime-telemetry-java17](../instrumentation/runtime-telemetry/runtime-telemetry-java17/library),
[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 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] | diff --git a/instrumentation/graphql-java-12.0/javaagent/build.gradle.kts b/instrumentation/graphql-java-12.0/javaagent/build.gradle.kts deleted file mode 100644 index 699820cf67a6..000000000000 --- a/instrumentation/graphql-java-12.0/javaagent/build.gradle.kts +++ /dev/null @@ -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")) -} diff --git a/instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/GraphQLTelemetry.java b/instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/GraphQLTelemetry.java deleted file mode 100644 index 1f9b0eb47034..000000000000 --- a/instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/GraphQLTelemetry.java +++ /dev/null @@ -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 instrumenter; - private final boolean sanitizeQuery; - - GraphQLTelemetry(OpenTelemetry openTelemetry, boolean sanitizeQuery) { - InstrumenterBuilder builder = - Instrumenter.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); - } -} diff --git a/instrumentation/graphql-java-12.0/javaagent/README.md b/instrumentation/graphql-java/README.md similarity index 100% rename from instrumentation/graphql-java-12.0/javaagent/README.md rename to instrumentation/graphql-java/README.md diff --git a/instrumentation/graphql-java/graphql-java-12.0/javaagent/build.gradle.kts b/instrumentation/graphql-java/graphql-java-12.0/javaagent/build.gradle.kts new file mode 100644 index 000000000000..c7e0cff0920a --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-12.0/javaagent/build.gradle.kts @@ -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.+") +} diff --git a/instrumentation/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlInstrumentation.java b/instrumentation/graphql-java/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlInstrumentation.java similarity index 95% rename from instrumentation/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlInstrumentation.java rename to instrumentation/graphql-java/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlInstrumentation.java index 70f13a49f656..543efefaab08 100644 --- a/instrumentation/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlInstrumentation.java +++ b/instrumentation/graphql-java/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlInstrumentation.java @@ -17,7 +17,7 @@ import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; -public class GraphqlInstrumentation implements TypeInstrumentation { +class GraphqlInstrumentation implements TypeInstrumentation { @Override public ElementMatcher typeMatcher() { diff --git a/instrumentation/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlInstrumentationModule.java b/instrumentation/graphql-java/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlInstrumentationModule.java similarity index 65% rename from instrumentation/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlInstrumentationModule.java rename to instrumentation/graphql-java/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlInstrumentationModule.java index 2839845a500b..1df81f84334d 100644 --- a/instrumentation/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlInstrumentationModule.java +++ b/instrumentation/graphql-java/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlInstrumentationModule.java @@ -5,11 +5,15 @@ 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 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) @@ -19,6 +23,13 @@ public GraphqlInstrumentationModule() { super("graphql-java", "graphql-java-12.0"); } + @Override + public ElementMatcher.Junction classLoaderMatcher() { + // added in 20.0 + return not( + hasClassesNamed("graphql.execution.instrumentation.SimplePerformantInstrumentation")); + } + @Override public List typeInstrumentations() { return Collections.singletonList(new GraphqlInstrumentation()); diff --git a/instrumentation/graphql-java/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlSingletons.java b/instrumentation/graphql-java/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlSingletons.java new file mode 100644 index 000000000000..41c64c6a6cc3 --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlSingletons.java @@ -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); + } +} diff --git a/instrumentation/graphql-java-12.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlTest.java b/instrumentation/graphql-java/graphql-java-12.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlTest.java similarity index 100% rename from instrumentation/graphql-java-12.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlTest.java rename to instrumentation/graphql-java/graphql-java-12.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlTest.java diff --git a/instrumentation/graphql-java-12.0/library/README.md b/instrumentation/graphql-java/graphql-java-12.0/library/README.md similarity index 94% rename from instrumentation/graphql-java-12.0/library/README.md rename to instrumentation/graphql-java/graphql-java-12.0/library/README.md index 40ef49479d3c..447473d1df20 100644 --- a/instrumentation/graphql-java-12.0/library/README.md +++ b/instrumentation/graphql-java/graphql-java-12.0/library/README.md @@ -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/). diff --git a/instrumentation/graphql-java/graphql-java-12.0/library/build.gradle.kts b/instrumentation/graphql-java/graphql-java-12.0/library/build.gradle.kts new file mode 100644 index 000000000000..052455bdda7a --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-12.0/library/build.gradle.kts @@ -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.+") +} diff --git a/instrumentation/graphql-java/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/GraphQLTelemetry.java b/instrumentation/graphql-java/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/GraphQLTelemetry.java new file mode 100644 index 000000000000..4671cce91cfb --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/GraphQLTelemetry.java @@ -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); + } +} diff --git a/instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/GraphQLTelemetryBuilder.java b/instrumentation/graphql-java/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/GraphQLTelemetryBuilder.java similarity index 100% rename from instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/GraphQLTelemetryBuilder.java rename to instrumentation/graphql-java/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/GraphQLTelemetryBuilder.java diff --git a/instrumentation/graphql-java/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/OpenTelemetryInstrumentation.java b/instrumentation/graphql-java/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/OpenTelemetryInstrumentation.java new file mode 100644 index 000000000000..01a784a96c17 --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/OpenTelemetryInstrumentation.java @@ -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 beginExecution( + InstrumentationExecutionParameters parameters) { + OpenTelemetryInstrumentationState state = parameters.getInstrumentationState(); + return helper.beginExecution(state); + } + + @Override + public InstrumentationContext 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); + } +} diff --git a/instrumentation/graphql-java-12.0/library/src/test/java/io/opentelemetry/instrumentation/graphql/v12_0/GraphqlTest.java b/instrumentation/graphql-java/graphql-java-12.0/library/src/test/java/io/opentelemetry/instrumentation/graphql/v12_0/GraphqlTest.java similarity index 100% rename from instrumentation/graphql-java-12.0/library/src/test/java/io/opentelemetry/instrumentation/graphql/v12_0/GraphqlTest.java rename to instrumentation/graphql-java/graphql-java-12.0/library/src/test/java/io/opentelemetry/instrumentation/graphql/v12_0/GraphqlTest.java diff --git a/instrumentation/graphql-java/graphql-java-20.0/javaagent/build.gradle.kts b/instrumentation/graphql-java/graphql-java-20.0/javaagent/build.gradle.kts new file mode 100644 index 000000000000..e2fbbac15a29 --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-20.0/javaagent/build.gradle.kts @@ -0,0 +1,30 @@ +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")) +} + +if (findProperty("testLatestDeps") as Boolean) { + otelJava { + minJavaVersionSupported.set(JavaVersion.VERSION_11) + } +} diff --git a/instrumentation/graphql-java/graphql-java-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v20_0/GraphqlInstrumentation.java b/instrumentation/graphql-java/graphql-java-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v20_0/GraphqlInstrumentation.java new file mode 100644 index 000000000000..07b891bd52f2 --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v20_0/GraphqlInstrumentation.java @@ -0,0 +1,115 @@ +/* + * 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.none; + +import com.google.errorprone.annotations.CanIgnoreReturnValue; +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.asm.AsmVisitorWrapper; +import net.bytebuddy.description.field.FieldDescription; +import net.bytebuddy.description.field.FieldList; +import net.bytebuddy.description.method.MethodList; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.Implementation; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.pool.TypePool; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +class GraphqlInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("graphql.GraphQL"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + none(), this.getClass().getName() + "$AddInstrumentationAdvice"); + + transformer.applyTransformer( + (builder, typeDescription, classLoader, javaModule, protectionDomain) -> + builder.visit( + new AsmVisitorWrapper() { + @Override + public int mergeWriter(int flags) { + return flags; + } + + @Override + @CanIgnoreReturnValue + public int mergeReader(int flags) { + return flags; + } + + @Override + public ClassVisitor wrap( + TypeDescription instrumentedType, + ClassVisitor classVisitor, + Implementation.Context implementationContext, + TypePool typePool, + FieldList fields, + MethodList methods, + int writerFlags, + int readerFlags) { + return new ClassVisitor(Opcodes.ASM9, classVisitor) { + @Override + public MethodVisitor visitMethod( + int access, + String name, + String descriptor, + String signature, + String[] exceptions) { + MethodVisitor mv = + super.visitMethod(access, name, descriptor, signature, exceptions); + if ("".equals(name) + && "(Lgraphql/GraphQL$Builder;)V".equals(descriptor)) { + return new MethodVisitor(api, mv) { + @Override + public void visitFieldInsn( + int opcode, String owner, String name, String descriptor) { + // Call GraphqlSingletons.addInstrumentation on the value before it is + // written to the instrumentation field + if (opcode == Opcodes.PUTFIELD + && "instrumentation".equals(name) + && "Lgraphql/execution/instrumentation/Instrumentation;" + .equals(descriptor)) { + mv.visitMethodInsn( + Opcodes.INVOKESTATIC, + Type.getInternalName(GraphqlSingletons.class), + "addInstrumentation", + "(Lgraphql/execution/instrumentation/Instrumentation;)Lgraphql/execution/instrumentation/Instrumentation;", + false); + } + super.visitFieldInsn(opcode, owner, name, descriptor); + } + }; + } + return mv; + } + }; + } + })); + } + + @SuppressWarnings("unused") + public static class AddInstrumentationAdvice { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void onExit(@Advice.Return(readOnly = false) Instrumentation instrumentation) { + // this advice is here only to get GraphqlSingletons injected and checked by muzzle + instrumentation = addInstrumentation(instrumentation); + } + } +} diff --git a/instrumentation/graphql-java/graphql-java-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v20_0/GraphqlInstrumentationModule.java b/instrumentation/graphql-java/graphql-java-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v20_0/GraphqlInstrumentationModule.java new file mode 100644 index 000000000000..160a03e89811 --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v20_0/GraphqlInstrumentationModule.java @@ -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 classLoaderMatcher() { + // added in 20.0 + return hasClassesNamed("graphql.execution.instrumentation.SimplePerformantInstrumentation"); + } + + @Override + public List typeInstrumentations() { + return Collections.singletonList(new GraphqlInstrumentation()); + } +} diff --git a/instrumentation/graphql-java/graphql-java-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v20_0/GraphqlSingletons.java b/instrumentation/graphql-java/graphql-java-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v20_0/GraphqlSingletons.java new file mode 100644 index 000000000000..c16ffc951afa --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-20.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v20_0/GraphqlSingletons.java @@ -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); + } +} diff --git a/instrumentation/graphql-java/graphql-java-20.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/graphql/v20_0/GraphqlTest.java b/instrumentation/graphql-java/graphql-java-20.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/graphql/v20_0/GraphqlTest.java new file mode 100644 index 000000000000..0c2fdde2ce90 --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-20.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/graphql/v20_0/GraphqlTest.java @@ -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) {} +} diff --git a/instrumentation/graphql-java/graphql-java-20.0/library/README.md b/instrumentation/graphql-java/graphql-java-20.0/library/README.md new file mode 100644 index 000000000000..2482e76ee8d9 --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-20.0/library/README.md @@ -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 + + + io.opentelemetry.instrumentation + opentelemetry-graphql-java-20.0 + OPENTELEMETRY_VERSION + + +``` + +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()); +} +``` diff --git a/instrumentation/graphql-java/graphql-java-20.0/library/build.gradle.kts b/instrumentation/graphql-java/graphql-java-20.0/library/build.gradle.kts new file mode 100644 index 000000000000..79f49cf21985 --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-20.0/library/build.gradle.kts @@ -0,0 +1,16 @@ +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")) +} + +if (findProperty("testLatestDeps") as Boolean) { + otelJava { + minJavaVersionSupported.set(JavaVersion.VERSION_11) + } +} diff --git a/instrumentation/graphql-java/graphql-java-20.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v20_0/GraphQLTelemetry.java b/instrumentation/graphql-java/graphql-java-20.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v20_0/GraphQLTelemetry.java new file mode 100644 index 000000000000..761a54e55ceb --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-20.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v20_0/GraphQLTelemetry.java @@ -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); + } +} diff --git a/instrumentation/graphql-java/graphql-java-20.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v20_0/GraphQLTelemetryBuilder.java b/instrumentation/graphql-java/graphql-java-20.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v20_0/GraphQLTelemetryBuilder.java new file mode 100644 index 000000000000..24c9c9b63e99 --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-20.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v20_0/GraphQLTelemetryBuilder.java @@ -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); + } +} diff --git a/instrumentation/graphql-java/graphql-java-20.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v20_0/OpenTelemetryInstrumentation.java b/instrumentation/graphql-java/graphql-java-20.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v20_0/OpenTelemetryInstrumentation.java new file mode 100644 index 000000000000..965a61e5a8f0 --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-20.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v20_0/OpenTelemetryInstrumentation.java @@ -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 beginExecution( + InstrumentationExecutionParameters parameters, InstrumentationState rawState) { + OpenTelemetryInstrumentationState state = ofState(rawState); + return helper.beginExecution(state); + } + + @Override + public InstrumentationContext 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); + } +} diff --git a/instrumentation/graphql-java/graphql-java-20.0/library/src/test/java/io/opentelemetry/instrumentation/graphql/v20_0/GraphqlTest.java b/instrumentation/graphql-java/graphql-java-20.0/library/src/test/java/io/opentelemetry/instrumentation/graphql/v20_0/GraphqlTest.java new file mode 100644 index 000000000000..4c7ee40b9f9a --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-20.0/library/src/test/java/io/opentelemetry/instrumentation/graphql/v20_0/GraphqlTest.java @@ -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()); + } +} diff --git a/instrumentation/graphql-java/graphql-java-common/javaagent/build.gradle.kts b/instrumentation/graphql-java/graphql-java-common/javaagent/build.gradle.kts new file mode 100644 index 000000000000..09b7a2ee7f2f --- /dev/null +++ b/instrumentation/graphql-java/graphql-java-common/javaagent/build.gradle.kts @@ -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") +} diff --git a/instrumentation/graphql-java-12.0/library/build.gradle.kts b/instrumentation/graphql-java/graphql-java-common/library/build.gradle.kts similarity index 59% rename from instrumentation/graphql-java-12.0/library/build.gradle.kts rename to instrumentation/graphql-java/graphql-java-common/library/build.gradle.kts index 5674a1bdb25b..9e5a30f72f68 100644 --- a/instrumentation/graphql-java-12.0/library/build.gradle.kts +++ b/instrumentation/graphql-java/graphql-java-common/library/build.gradle.kts @@ -4,6 +4,4 @@ plugins { dependencies { library("com.graphql-java:graphql-java:12.0") - - testImplementation(project(":instrumentation:graphql-java-12.0:testing")) } diff --git a/instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/GraphqlAttributesExtractor.java b/instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/GraphqlAttributesExtractor.java similarity index 63% rename from instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/GraphqlAttributesExtractor.java rename to instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/GraphqlAttributesExtractor.java index 952f10c5f339..3b7cdc735283 100644 --- a/instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/GraphqlAttributesExtractor.java +++ b/instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/GraphqlAttributesExtractor.java @@ -3,10 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.graphql.v12_0; +package io.opentelemetry.instrumentation.graphql.internal; import graphql.ExecutionResult; -import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.Context; @@ -14,8 +13,12 @@ import java.util.Locale; 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 - implements AttributesExtractor { + implements AttributesExtractor { // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/database/graphql.md private static final AttributeKey OPERATION_NAME = AttributeKey.stringKey("graphql.operation.name"); @@ -28,20 +31,19 @@ final class GraphqlAttributesExtractor public void onStart( AttributesBuilder attributes, Context parentContext, - InstrumentationExecutionParameters request) {} + OpenTelemetryInstrumentationState request) {} @Override public void onEnd( AttributesBuilder attributes, Context context, - InstrumentationExecutionParameters request, + OpenTelemetryInstrumentationState request, @Nullable ExecutionResult response, @Nullable Throwable error) { - OpenTelemetryInstrumentationState state = request.getInstrumentationState(); - attributes.put(OPERATION_NAME, state.getOperationName()); - if (state.getOperation() != null) { - attributes.put(OPERATION_TYPE, state.getOperation().name().toLowerCase(Locale.ROOT)); + attributes.put(OPERATION_NAME, request.getOperationName()); + if (request.getOperation() != null) { + attributes.put(OPERATION_TYPE, request.getOperation().name().toLowerCase(Locale.ROOT)); } - attributes.put(GRAPHQL_DOCUMENT, state.getQuery()); + attributes.put(GRAPHQL_DOCUMENT, request.getQuery()); } } diff --git a/instrumentation/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlSingletons.java b/instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/InstrumentationUtil.java similarity index 54% rename from instrumentation/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlSingletons.java rename to instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/InstrumentationUtil.java index 74beb880b454..572b166d2969 100644 --- a/instrumentation/graphql-java-12.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/graphql/v12_0/GraphqlSingletons.java +++ b/instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/InstrumentationUtil.java @@ -3,31 +3,21 @@ * 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.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.List; -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() {} +/** + * 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 { - public static Instrumentation addInstrumentation(Instrumentation instrumentation) { - Instrumentation ourInstrumentation = TELEMETRY.newInstrumentation(); + public static Instrumentation addInstrumentation( + Instrumentation instrumentation, Instrumentation ourInstrumentation) { if (instrumentation == null) { return ourInstrumentation; } @@ -47,4 +37,6 @@ public static Instrumentation addInstrumentation(Instrumentation instrumentation } return new ChainedInstrumentation(instrumentationList); } + + private InstrumentationUtil() {} } diff --git a/instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/OpenTelemetryInstrumentation.java b/instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/OpenTelemetryInstrumentationHelper.java similarity index 65% rename from instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/OpenTelemetryInstrumentation.java rename to instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/OpenTelemetryInstrumentationHelper.java index e93748551b04..46b66fd72ca1 100644 --- a/instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/OpenTelemetryInstrumentation.java +++ b/instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/OpenTelemetryInstrumentationHelper.java @@ -3,17 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.graphql.v12_0; +package io.opentelemetry.instrumentation.graphql.internal; import graphql.ExecutionResult; import graphql.GraphQLError; import graphql.execution.instrumentation.InstrumentationContext; -import graphql.execution.instrumentation.InstrumentationState; -import graphql.execution.instrumentation.SimpleInstrumentation; import graphql.execution.instrumentation.SimpleInstrumentationContext; 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.AstTransformer; import graphql.language.BooleanValue; @@ -23,52 +19,75 @@ import graphql.language.NodeVisitorStub; import graphql.language.NullValue; import graphql.language.OperationDefinition; -import graphql.language.OperationDefinition.Operation; import graphql.language.Value; import graphql.language.VariableReference; import graphql.schema.DataFetcher; import graphql.util.TraversalControl; import graphql.util.TraverserContext; import graphql.util.TreeTransformerUtil; +import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.StatusCode; import io.opentelemetry.context.Context; import io.opentelemetry.context.Scope; 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 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 AstTransformer astTransformer = new AstTransformer(); - private final Instrumenter instrumenter; + private final Instrumenter instrumenter; private final boolean sanitizeQuery; - OpenTelemetryInstrumentation( - Instrumenter instrumenter, + private OpenTelemetryInstrumentationHelper( + Instrumenter instrumenter, boolean sanitizeQuery) { this.instrumenter = instrumenter; this.sanitizeQuery = sanitizeQuery; } - @Override - public InstrumentationState createState() { - return new OpenTelemetryInstrumentationState(); + public static OpenTelemetryInstrumentationHelper create( + OpenTelemetry openTelemetry, String instrumentationName, boolean sanitizeQuery) { + InstrumenterBuilder builder = + Instrumenter.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 beginExecution( - InstrumentationExecutionParameters parameters) { + OpenTelemetryInstrumentationState state) { Context parentContext = Context.current(); - if (!instrumenter.shouldStart(parentContext, parameters)) { + if (!instrumenter.shouldStart(parentContext, state)) { return SimpleInstrumentationContext.noOp(); } - Context context = instrumenter.start(parentContext, parameters); - OpenTelemetryInstrumentationState state = parameters.getInstrumentationState(); + Context context = instrumenter.start(parentContext, state); state.setContext(context); return SimpleInstrumentationContext.whenCompleted( @@ -82,20 +101,18 @@ public InstrumentationContext beginExecution( span.addEvent(SemanticAttributes.EXCEPTION_EVENT_NAME, attributes.build()); } - instrumenter.end(context, parameters, result, throwable); + instrumenter.end(context, state, result, throwable); }); } - @Override public InstrumentationContext beginExecuteOperation( - InstrumentationExecuteOperationParameters parameters) { - - OpenTelemetryInstrumentationState state = parameters.getInstrumentationState(); + InstrumentationExecuteOperationParameters parameters, + OpenTelemetryInstrumentationState state) { Span span = Span.fromContext(state.getContext()); OperationDefinition operationDefinition = parameters.getExecutionContext().getOperationDefinition(); - Operation operation = operationDefinition.getOperation(); + OperationDefinition.Operation operation = operationDefinition.getOperation(); String operationType = operation.name().toLowerCase(Locale.ROOT); String operationName = operationDefinition.getName(); @@ -117,10 +134,8 @@ public InstrumentationContext beginExecuteOperation( return SimpleInstrumentationContext.noOp(); } - @Override public DataFetcher instrumentDataFetcher( - DataFetcher dataFetcher, InstrumentationFieldFetchParameters parameters) { - OpenTelemetryInstrumentationState state = parameters.getInstrumentationState(); + DataFetcher dataFetcher, OpenTelemetryInstrumentationState state) { Context context = state.getContext(); return (DataFetcher) diff --git a/instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/OpenTelemetryInstrumentationState.java b/instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/OpenTelemetryInstrumentationState.java similarity index 76% rename from instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/OpenTelemetryInstrumentationState.java rename to instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/OpenTelemetryInstrumentationState.java index 10f453e7ba79..c540eb55f2d5 100644 --- a/instrumentation/graphql-java-12.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v12_0/OpenTelemetryInstrumentationState.java +++ b/instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/OpenTelemetryInstrumentationState.java @@ -3,13 +3,17 @@ * 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.language.OperationDefinition.Operation; 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 Operation operation; private String operationName; diff --git a/instrumentation/graphql-java-12.0/testing/build.gradle.kts b/instrumentation/graphql-java/graphql-java-common/testing/build.gradle.kts similarity index 100% rename from instrumentation/graphql-java-12.0/testing/build.gradle.kts rename to instrumentation/graphql-java/graphql-java-common/testing/build.gradle.kts diff --git a/instrumentation/graphql-java-12.0/testing/src/main/java/io/opentelemetry/instrumentation/graphql/AbstractGraphqlTest.java b/instrumentation/graphql-java/graphql-java-common/testing/src/main/java/io/opentelemetry/instrumentation/graphql/AbstractGraphqlTest.java similarity index 97% rename from instrumentation/graphql-java-12.0/testing/src/main/java/io/opentelemetry/instrumentation/graphql/AbstractGraphqlTest.java rename to instrumentation/graphql-java/graphql-java-common/testing/src/main/java/io/opentelemetry/instrumentation/graphql/AbstractGraphqlTest.java index a0a8e411e726..9b7afba9101e 100644 --- a/instrumentation/graphql-java-12.0/testing/src/main/java/io/opentelemetry/instrumentation/graphql/AbstractGraphqlTest.java +++ b/instrumentation/graphql-java/graphql-java-common/testing/src/main/java/io/opentelemetry/instrumentation/graphql/AbstractGraphqlTest.java @@ -180,7 +180,7 @@ void successfulQueryWithoutName() { equalTo(AttributeKey.stringKey("graphql.operation.type"), "query"), normalizedQueryEqualsTo( AttributeKey.stringKey("graphql.document"), - "query { bookById(id: ?) { name } }")), + "{ bookById(id: ?) { name } }")), span -> span.hasName("fetchBookById").hasParent(trace.getSpan(0)))); } @@ -211,7 +211,8 @@ void parseError() { satisfies( SemanticAttributes.EXCEPTION_MESSAGE, message -> - message.startsWith("Invalid Syntax")))))); + message.startsWithIgnoringCase( + "Invalid Syntax")))))); } @Test @@ -252,8 +253,7 @@ void validationError() { satisfies( SemanticAttributes.EXCEPTION_MESSAGE, message -> - message.startsWith( - "Validation error of type FieldUndefined")))))); + message.startsWith("Validation error")))))); } @Test @@ -295,6 +295,9 @@ private static AttributeAssertion normalizedQueryEqualsTo( stringAssert.satisfies( querySource -> { String normalized = querySource.replaceAll("(?s)\\s+", " "); + if (normalized.startsWith("query {")) { + normalized = normalized.substring("query ".length()); + } assertThat(normalized).isEqualTo(value); })); } diff --git a/instrumentation/graphql-java-12.0/testing/src/main/resources/schema.graphqls b/instrumentation/graphql-java/graphql-java-common/testing/src/main/resources/schema.graphqls similarity index 100% rename from instrumentation/graphql-java-12.0/testing/src/main/resources/schema.graphqls rename to instrumentation/graphql-java/graphql-java-common/testing/src/main/resources/schema.graphqls diff --git a/settings.gradle.kts b/settings.gradle.kts index 87049148b6fc..309ad356356b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -180,9 +180,12 @@ include(":instrumentation:cassandra:cassandra-4.4:library") include(":instrumentation:cassandra:cassandra-4.4:testing") include(":instrumentation:cassandra:cassandra-4-common:testing") include(":instrumentation:cdi-testing") -include(":instrumentation:graphql-java-12.0:javaagent") -include(":instrumentation:graphql-java-12.0:library") -include(":instrumentation:graphql-java-12.0:testing") +include(":instrumentation:graphql-java:graphql-java-12.0:javaagent") +include(":instrumentation:graphql-java:graphql-java-12.0:library") +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:javaagent") include(":instrumentation:internal:internal-class-loader:javaagent")