diff --git a/instrumentation/graphql-java/README.md b/instrumentation/graphql-java/README.md index 65693b8e5e42..ba7bb42d6712 100644 --- a/instrumentation/graphql-java/README.md +++ b/instrumentation/graphql-java/README.md @@ -4,6 +4,7 @@ |------------------------------------------------------------------------|---------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `otel.instrumentation.graphql.query-sanitizer.enabled` | Boolean | `true` | Whether to remove sensitive information from query source that is added as span attribute. | | `otel.instrumentation.graphql.add-operation-name-to-span-name.enabled` | Boolean | `false` | Whether GraphQL operation name is added to the span name.

**WARNING**: GraphQL operation name is provided by the client and can have high cardinality. Use only when the server is not exposed to malicious clients. | +| `otel.instrumentation.graphql.capture-query` | Boolean | `true` | Whether to capture the query in `graphql.document` span attribute. | # Settings for the GraphQL 20 instrumentation 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 index 122a3b388242..79e0130d3129 100644 --- 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 @@ -13,6 +13,9 @@ public final class GraphqlSingletons { + private static final boolean CAPTURE_QUERY = + AgentInstrumentationConfig.get() + .getBoolean("otel.instrumentation.graphql.capture-query", true); private static final boolean QUERY_SANITIZATION_ENABLED = AgentInstrumentationConfig.get() .getBoolean("otel.instrumentation.graphql.query-sanitizer.enabled", true); @@ -23,6 +26,7 @@ public final class GraphqlSingletons { private static final GraphQLTelemetry TELEMETRY = GraphQLTelemetry.builder(GlobalOpenTelemetry.get()) + .setCaptureQuery(CAPTURE_QUERY) .setSanitizeQuery(QUERY_SANITIZATION_ENABLED) .setAddOperationNameToSpanName(ADD_OPERATION_NAME_TO_SPAN_NAME) .build(); 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 index 6b7cbb926fda..cddf7dac4d35 100644 --- 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 @@ -28,10 +28,17 @@ public static GraphQLTelemetryBuilder builder(OpenTelemetry openTelemetry) { private final OpenTelemetryInstrumentationHelper helper; GraphQLTelemetry( - OpenTelemetry openTelemetry, boolean sanitizeQuery, boolean addOperationNameToSpanName) { + OpenTelemetry openTelemetry, + boolean captureQuery, + boolean sanitizeQuery, + boolean addOperationNameToSpanName) { helper = OpenTelemetryInstrumentationHelper.create( - openTelemetry, INSTRUMENTATION_NAME, sanitizeQuery, addOperationNameToSpanName); + openTelemetry, + INSTRUMENTATION_NAME, + captureQuery, + sanitizeQuery, + addOperationNameToSpanName); } /** diff --git a/instrumentation/graphql-java/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 index aa2f47dc6265..adeaab18ea97 100644 --- a/instrumentation/graphql-java/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 @@ -14,6 +14,7 @@ public final class GraphQLTelemetryBuilder { private final OpenTelemetry openTelemetry; + private boolean captureQuery = true; private boolean sanitizeQuery = true; private boolean addOperationNameToSpanName = false; @@ -21,6 +22,16 @@ public final class GraphQLTelemetryBuilder { this.openTelemetry = openTelemetry; } + /** + * Sets whether query should be captured in {@code graphql.document} span attribute. Default is + * {@code true}. + */ + @CanIgnoreReturnValue + public GraphQLTelemetryBuilder setCaptureQuery(boolean captureQuery) { + this.captureQuery = captureQuery; + return this; + } + /** Sets whether sensitive information should be removed from queries. Default is {@code true}. */ @CanIgnoreReturnValue public GraphQLTelemetryBuilder setSanitizeQuery(boolean sanitizeQuery) { @@ -45,6 +56,7 @@ public GraphQLTelemetryBuilder setAddOperationNameToSpanName(boolean addOperatio * GraphQLTelemetryBuilder}. */ public GraphQLTelemetry build() { - return new GraphQLTelemetry(openTelemetry, sanitizeQuery, addOperationNameToSpanName); + return new GraphQLTelemetry( + openTelemetry, captureQuery, sanitizeQuery, addOperationNameToSpanName); } } 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 index ba489efeea72..99845529f985 100644 --- 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 @@ -13,6 +13,9 @@ public final class GraphqlSingletons { + private static final boolean CAPTURE_QUERY = + AgentInstrumentationConfig.get() + .getBoolean("otel.instrumentation.graphql.capture-query", true); private static final boolean QUERY_SANITIZATION_ENABLED = AgentInstrumentationConfig.get() .getBoolean("otel.instrumentation.graphql.query-sanitizer.enabled", true); @@ -29,6 +32,7 @@ public final class GraphqlSingletons { private static final GraphQLTelemetry TELEMETRY = GraphQLTelemetry.builder(GlobalOpenTelemetry.get()) + .setCaptureQuery(CAPTURE_QUERY) .setSanitizeQuery(QUERY_SANITIZATION_ENABLED) .setDataFetcherInstrumentationEnabled(DATA_FETCHER_ENABLED) .setTrivialDataFetcherInstrumentationEnabled(TRIVIAL_DATA_FETCHER_ENABLED) 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 index 98905483858d..1c697462bbde 100644 --- 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 @@ -32,13 +32,14 @@ public static GraphQLTelemetryBuilder builder(OpenTelemetry openTelemetry) { GraphQLTelemetry( OpenTelemetry openTelemetry, + boolean captureQuery, boolean sanitizeQuery, Instrumenter dataFetcherInstrumenter, boolean createSpansForTrivialDataFetcher, boolean addOperationNameToSpanName) { helper = GraphqlInstrumenterFactory.createInstrumentationHelper( - openTelemetry, sanitizeQuery, addOperationNameToSpanName); + openTelemetry, captureQuery, sanitizeQuery, addOperationNameToSpanName); this.dataFetcherInstrumenter = dataFetcherInstrumenter; this.createSpansForTrivialDataFetcher = createSpansForTrivialDataFetcher; } 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 index f12d61dfaca5..e5f4a9625c9f 100644 --- 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 @@ -14,6 +14,7 @@ public final class GraphQLTelemetryBuilder { private final OpenTelemetry openTelemetry; + private boolean captureQuery = true; private boolean sanitizeQuery = true; private boolean dataFetcherInstrumentationEnabled = false; private boolean trivialDataFetcherInstrumentationEnabled = false; @@ -23,6 +24,16 @@ public final class GraphQLTelemetryBuilder { this.openTelemetry = openTelemetry; } + /** + * Sets whether query should be captured in {@code graphql.document} span attribute. Default is + * {@code true}. + */ + @CanIgnoreReturnValue + public GraphQLTelemetryBuilder setCaptureQuery(boolean captureQuery) { + this.captureQuery = captureQuery; + return this; + } + /** Sets whether sensitive information should be removed from queries. Default is {@code true}. */ @CanIgnoreReturnValue public GraphQLTelemetryBuilder setSanitizeQuery(boolean sanitizeQuery) { @@ -68,6 +79,7 @@ public GraphQLTelemetryBuilder setAddOperationNameToSpanName(boolean addOperatio public GraphQLTelemetry build() { return new GraphQLTelemetry( openTelemetry, + captureQuery, sanitizeQuery, GraphqlInstrumenterFactory.createDataFetcherInstrumenter( openTelemetry, dataFetcherInstrumentationEnabled), diff --git a/instrumentation/graphql-java/graphql-java-20.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v20_0/GraphqlInstrumenterFactory.java b/instrumentation/graphql-java/graphql-java-20.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v20_0/GraphqlInstrumenterFactory.java index 76414aed3281..d708ae69506e 100644 --- a/instrumentation/graphql-java/graphql-java-20.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v20_0/GraphqlInstrumenterFactory.java +++ b/instrumentation/graphql-java/graphql-java-20.0/library/src/main/java/io/opentelemetry/instrumentation/graphql/v20_0/GraphqlInstrumenterFactory.java @@ -18,9 +18,16 @@ final class GraphqlInstrumenterFactory { private static final String INSTRUMENTATION_NAME = "io.opentelemetry.graphql-java-20.0"; static OpenTelemetryInstrumentationHelper createInstrumentationHelper( - OpenTelemetry openTelemetry, boolean sanitizeQuery, boolean addOperationNameToSpanName) { + OpenTelemetry openTelemetry, + boolean captureQuery, + boolean sanitizeQuery, + boolean addOperationNameToSpanName) { return OpenTelemetryInstrumentationHelper.create( - openTelemetry, INSTRUMENTATION_NAME, sanitizeQuery, addOperationNameToSpanName); + openTelemetry, + INSTRUMENTATION_NAME, + captureQuery, + sanitizeQuery, + addOperationNameToSpanName); } static Instrumenter createDataFetcherInstrumenter( diff --git a/instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/GraphqlAttributesExtractor.java b/instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/GraphqlAttributesExtractor.java index b8cd9737c422..5cea4d349f78 100644 --- a/instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/GraphqlAttributesExtractor.java +++ b/instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/GraphqlAttributesExtractor.java @@ -45,6 +45,8 @@ public void onEnd( attributes.put( GRAPHQL_OPERATION_TYPE, request.getOperation().name().toLowerCase(Locale.ROOT)); } - attributes.put(GRAPHQL_DOCUMENT, request.getQuery()); + if (request.getQuery() != null) { + attributes.put(GRAPHQL_DOCUMENT, request.getQuery()); + } } } diff --git a/instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/OpenTelemetryInstrumentationHelper.java b/instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/OpenTelemetryInstrumentationHelper.java index 8d7f8231a058..e57f88fe515b 100644 --- a/instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/OpenTelemetryInstrumentationHelper.java +++ b/instrumentation/graphql-java/graphql-java-common/library/src/main/java/io/opentelemetry/instrumentation/graphql/internal/OpenTelemetryInstrumentationHelper.java @@ -47,14 +47,17 @@ public final class OpenTelemetryInstrumentationHelper { private static final AstTransformer astTransformer = new AstTransformer(); private final Instrumenter instrumenter; + private final boolean captureQuery; private final boolean sanitizeQuery; private final boolean addOperationNameToSpanName; private OpenTelemetryInstrumentationHelper( Instrumenter instrumenter, + boolean captureQuery, boolean sanitizeQuery, boolean addOperationNameToSpanName) { this.instrumenter = instrumenter; + this.captureQuery = captureQuery; this.sanitizeQuery = sanitizeQuery; this.addOperationNameToSpanName = addOperationNameToSpanName; } @@ -62,6 +65,7 @@ private OpenTelemetryInstrumentationHelper( public static OpenTelemetryInstrumentationHelper create( OpenTelemetry openTelemetry, String instrumentationName, + boolean captureQuery, boolean sanitizeQuery, boolean addOperationNameToSpanName) { InstrumenterBuilder builder = @@ -83,7 +87,7 @@ public static OpenTelemetryInstrumentationHelper create( builder.addAttributesExtractor(new GraphqlAttributesExtractor()); return new OpenTelemetryInstrumentationHelper( - builder.buildInstrumenter(), sanitizeQuery, addOperationNameToSpanName); + builder.buildInstrumenter(), captureQuery, sanitizeQuery, addOperationNameToSpanName); } public InstrumentationContext beginExecution( @@ -133,11 +137,13 @@ public InstrumentationContext beginExecuteOperation( state.setOperation(operation); state.setOperationName(operationName); - Node node = operationDefinition; - if (sanitizeQuery) { - node = sanitize(node); + if (captureQuery) { + Node node = operationDefinition; + if (sanitizeQuery) { + node = sanitize(node); + } + state.setQuery(AstPrinter.printAstCompact(node)); } - state.setQuery(AstPrinter.printAst(node)); return SimpleInstrumentationContext.noOp(); } diff --git a/instrumentation/graphql-java/graphql-java-common/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 index e0b40616d7ae..46ec58c8017a 100644 --- a/instrumentation/graphql-java/graphql-java-common/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 @@ -356,11 +356,22 @@ protected static AttributeAssertion normalizedQueryEqualsTo( stringAssert -> stringAssert.satisfies( querySource -> { - String normalized = querySource.replaceAll("(?s)\\s+", " "); - if (normalized.startsWith("query {")) { - normalized = normalized.substring("query ".length()); - } - assertThat(normalized).isEqualTo(value); + String normalized = normalizeQuery(querySource); + String valueNormalized = normalizeQuery(value); + assertThat(normalized).isEqualTo(valueNormalized); })); } + + private static String normalizeQuery(String query) { + if (query == null) { + return null; + } + + String normalized = + query.replaceAll("(?s)\\s+", " ").replaceAll("([{:,]) ", "$1").replaceAll(" ([{}])", "$1"); + if (normalized.startsWith("query{")) { + normalized = normalized.substring("query".length()); + } + return normalized; + } }