Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update GraphQL instrumentation to match spec #6179

Merged
merged 4 commits into from
Jun 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion instrumentation/graphql-java-12.0/javaagent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@

| System property | Type | Default | Description |
|---|---|---------|--------------------------------------------------------------------------------------------|
| `otel.instrumentation.graphql.experimental-span-attributes` | Boolean | `false` | Enable the capture of experimental span attributes. |
| `otel.instrumentation.graphql.query-sanitizer.enabled` | Boolean | `true` | Whether to remove sensitive information from query source that is added as span attribute. |
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,3 @@ dependencies {

testImplementation(project(":instrumentation:graphql-java-12.0:testing"))
}

tasks.withType<Test>().configureEach {
jvmArgs("-Dotel.instrumentation.graphql.experimental-span-attributes=true")
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@ public final class GraphqlSingletons {

private static final boolean QUERY_SANITIZATION_ENABLED =
Config.get().getBoolean("otel.instrumentation.graphql.query-sanitizer.enabled", true);
private static final boolean CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES =
Config.get().getBoolean("otel.instrumentation.graphql.experimental-span-attributes", false);

private static final GraphQLTelemetry TELEMETRY =
GraphQLTelemetry.builder(GlobalOpenTelemetry.get())
.setCaptureExperimentalSpanAttributes(CAPTURE_EXPERIMENTAL_SPAN_ATTRIBUTES)
.setSanitizeQuery(QUERY_SANITIZATION_ENABLED)
.build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,12 @@ public static GraphQLTelemetryBuilder builder(OpenTelemetry openTelemetry) {
}

private final Instrumenter<InstrumentationExecutionParameters, ExecutionResult> instrumenter;
private final boolean captureExperimentalSpanAttributes;
private final boolean sanitizeQuery;

GraphQLTelemetry(
OpenTelemetry openTelemetry,
boolean captureExperimentalSpanAttributes,
boolean sanitizeQuery) {
GraphQLTelemetry(OpenTelemetry openTelemetry, boolean sanitizeQuery) {
InstrumenterBuilder<InstrumentationExecutionParameters, ExecutionResult> builder =
Instrumenter.<InstrumentationExecutionParameters, ExecutionResult>builder(
openTelemetry, INSTRUMENTATION_NAME, ignored -> "GraphQL Query")
openTelemetry, INSTRUMENTATION_NAME, ignored -> "GraphQL Operation")
.setSpanStatusExtractor(
(spanStatusBuilder, instrumentationExecutionParameters, executionResult, error) -> {
if (!executionResult.getErrors().isEmpty()) {
Expand All @@ -54,20 +50,16 @@ public static GraphQLTelemetryBuilder builder(OpenTelemetry openTelemetry) {
error);
}
});
if (captureExperimentalSpanAttributes) {
builder.addAttributesExtractor(new ExperimentalAttributesExtractor());
}
builder.addAttributesExtractor(new GraphqlAttributesExtractor());

this.instrumenter = builder.newInstrumenter();
this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes;
this.sanitizeQuery = sanitizeQuery;
}

/**
* Returns a new {@link Instrumentation} that generates telemetry for received GraphQL requests.
*/
public Instrumentation newInstrumentation() {
return new OpenTelemetryInstrumentation(
instrumenter, captureExperimentalSpanAttributes, sanitizeQuery);
return new OpenTelemetryInstrumentation(instrumenter, sanitizeQuery);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ public final class GraphQLTelemetryBuilder {

private final OpenTelemetry openTelemetry;

private boolean captureExperimentalSpanAttributes;
private boolean sanitizeQuery = true;

GraphQLTelemetryBuilder(OpenTelemetry openTelemetry) {
Expand All @@ -25,9 +24,9 @@ public final class GraphQLTelemetryBuilder {
* removed in the future, so only enable this if you know you do not require attributes filled by
* this instrumentation to be stable across versions.
*/
@Deprecated
public GraphQLTelemetryBuilder setCaptureExperimentalSpanAttributes(
laurit marked this conversation as resolved.
Show resolved Hide resolved
boolean captureExperimentalSpanAttributes) {
this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes;
return this;
}

Expand All @@ -42,6 +41,6 @@ public GraphQLTelemetryBuilder setSanitizeQuery(boolean sanitizeQuery) {
* GraphQLTelemetryBuilder}.
*/
public GraphQLTelemetry build() {
return new GraphQLTelemetry(openTelemetry, captureExperimentalSpanAttributes, sanitizeQuery);
return new GraphQLTelemetry(openTelemetry, sanitizeQuery);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,18 @@
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import java.util.Locale;
import javax.annotation.Nullable;

final class ExperimentalAttributesExtractor
final class GraphqlAttributesExtractor
implements AttributesExtractor<InstrumentationExecutionParameters, ExecutionResult> {
// https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-graphql/src/enums/AttributeNames.ts
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/instrumentation/graphql.md
private static final AttributeKey<String> OPERATION_NAME =
AttributeKey.stringKey("graphql.operation.name");
private static final AttributeKey<String> OPERATION_TYPE =
AttributeKey.stringKey("graphql.operation.type");
private static final AttributeKey<String> GRAPHQL_SOURCE =
AttributeKey.stringKey("graphql.source");
private static final AttributeKey<String> GRAPHQL_DOCUMENT =
AttributeKey.stringKey("graphql.document");

@Override
public void onStart(
Expand All @@ -39,8 +40,8 @@ public void onEnd(
OpenTelemetryInstrumentationState state = request.getInstrumentationState();
attributes.put(OPERATION_NAME, state.getOperationName());
if (state.getOperation() != null) {
attributes.put(OPERATION_TYPE, state.getOperation().name());
attributes.put(OPERATION_TYPE, state.getOperation().name().toLowerCase(Locale.ROOT));
}
attributes.put(GRAPHQL_SOURCE, state.getQuery());
attributes.put(GRAPHQL_DOCUMENT, state.getQuery());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,19 @@
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.util.Locale;

final class OpenTelemetryInstrumentation extends SimpleInstrumentation {
private static final NodeVisitor sanitizingVisitor = new SanitizingVisitor();
private static final AstTransformer astTransformer = new AstTransformer();

private final Instrumenter<InstrumentationExecutionParameters, ExecutionResult> instrumenter;
private final boolean captureExperimentalSpanAttributes;
private final boolean sanitizeQuery;

OpenTelemetryInstrumentation(
Instrumenter<InstrumentationExecutionParameters, ExecutionResult> instrumenter,
boolean captureExperimentalSpanAttributes,
boolean sanitizeQuery) {
this.instrumenter = instrumenter;
this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes;
this.sanitizeQuery = sanitizeQuery;
}

Expand Down Expand Up @@ -98,24 +96,23 @@ public InstrumentationContext<ExecutionResult> beginExecuteOperation(
OperationDefinition operationDefinition =
parameters.getExecutionContext().getOperationDefinition();
Operation operation = operationDefinition.getOperation();
String operationType = operation.name().toLowerCase(Locale.ROOT);
String operationName = operationDefinition.getName();

String spanName = operation.name();
String spanName = operationType;
if (operationName != null && !operationName.isEmpty()) {
spanName += " " + operationName;
}
span.updateName(spanName);

if (captureExperimentalSpanAttributes) {
state.setOperation(operation);
state.setOperationName(operationName);
state.setOperation(operation);
state.setOperationName(operationName);

Node<?> node = operationDefinition;
if (sanitizeQuery) {
node = sanitize(node);
}
state.setQuery(AstPrinter.printAst(node));
Node<?> node = operationDefinition;
if (sanitizeQuery) {
node = sanitize(node);
}
state.setQuery(AstPrinter.printAst(node));

return SimpleInstrumentationContext.noOp();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ protected InstrumentationExtension getTesting() {

@Override
protected void configure(GraphQL.Builder builder) {
GraphQLTelemetry telemetry =
GraphQLTelemetry.builder(testing.getOpenTelemetry())
.setCaptureExperimentalSpanAttributes(true)
.build();
GraphQLTelemetry telemetry = GraphQLTelemetry.builder(testing.getOpenTelemetry()).build();
builder.instrumentation(telemetry.newInstrumentation());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,16 @@ void successfulQuery() {
trace ->
trace.hasSpansSatisfyingExactly(
span ->
span.hasName("QUERY findBookById")
span.hasName("query findBookById")
.hasKind(SpanKind.INTERNAL)
.hasNoParent()
.hasAttributesSatisfyingExactly(
equalTo(
AttributeKey.stringKey("graphql.operation.name"),
"findBookById"),
equalTo(AttributeKey.stringKey("graphql.operation.type"), "QUERY"),
equalTo(AttributeKey.stringKey("graphql.operation.type"), "query"),
normalizedQueryEqualsTo(
AttributeKey.stringKey("graphql.source"),
AttributeKey.stringKey("graphql.document"),
"query findBookById { bookById(id: ?) { name } }")),
span -> span.hasName("fetchBookById").hasParent(trace.getSpan(0))));
}
Expand All @@ -172,13 +172,13 @@ void successfulQueryWithoutName() {
trace ->
trace.hasSpansSatisfyingExactly(
span ->
span.hasName("QUERY")
span.hasName("query")
.hasKind(SpanKind.INTERNAL)
.hasNoParent()
.hasAttributesSatisfyingExactly(
equalTo(AttributeKey.stringKey("graphql.operation.type"), "QUERY"),
equalTo(AttributeKey.stringKey("graphql.operation.type"), "query"),
normalizedQueryEqualsTo(
AttributeKey.stringKey("graphql.source"),
AttributeKey.stringKey("graphql.document"),
"query { bookById(id: ?) { name } }")),
span -> span.hasName("fetchBookById").hasParent(trace.getSpan(0))));
}
Expand All @@ -195,7 +195,7 @@ void parseError() {
trace ->
trace.hasSpansSatisfyingExactly(
span ->
span.hasName("GraphQL Query")
span.hasName("GraphQL Operation")
.hasKind(SpanKind.INTERNAL)
.hasNoParent()
.hasAttributesSatisfying(Attributes::isEmpty)
Expand Down Expand Up @@ -241,7 +241,7 @@ void validationError() {
trace ->
trace.hasSpansSatisfyingExactly(
span ->
span.hasName("GraphQL Query")
span.hasName("GraphQL Operation")
.hasKind(SpanKind.INTERNAL)
.hasNoParent()
.hasAttributesSatisfying(Attributes::isEmpty)
Expand Down Expand Up @@ -286,16 +286,16 @@ void successfulMutation() {
trace ->
trace.hasSpansSatisfyingExactly(
span ->
span.hasName("MUTATION addNewBook")
span.hasName("mutation addNewBook")
.hasKind(SpanKind.INTERNAL)
.hasNoParent()
.hasAttributesSatisfyingExactly(
equalTo(
AttributeKey.stringKey("graphql.operation.name"), "addNewBook"),
equalTo(
AttributeKey.stringKey("graphql.operation.type"), "MUTATION"),
AttributeKey.stringKey("graphql.operation.type"), "mutation"),
normalizedQueryEqualsTo(
AttributeKey.stringKey("graphql.source"),
AttributeKey.stringKey("graphql.document"),
"mutation addNewBook { addBook(id: ?, name: ?, author: ?) { id } }"))));
}

Expand Down