Skip to content

Commit

Permalink
Added gRPC request instrumentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Tavh committed Oct 31, 2022
1 parent c170ec5 commit c6f544b
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.grpc.v1_6.GrpcTelemetry;
import io.opentelemetry.instrumentation.grpc.v1_6.internal.ContextStorageBridge;
import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig;

// Holds singleton references.
Expand All @@ -27,9 +28,11 @@ public final class GrpcSingletons {
InstrumentationConfig.get()
.getBoolean("otel.instrumentation.grpc.experimental-span-attributes", false);


GrpcTelemetry telemetry =
GrpcTelemetry.builder(GlobalOpenTelemetry.get())
.setCaptureExperimentalSpanAttributes(experimentalSpanAttributes)
.setRequestMetadataValuesToCapture(CommonConfig.get().getGrpcRequestMetadata())
.build();

CLIENT_INTERCEPTOR = telemetry.newClientInterceptor();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,34 @@
package io.opentelemetry.instrumentation.grpc.v1_6;

import io.grpc.Status;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import javax.annotation.Nullable;

import java.util.List;

import static io.opentelemetry.instrumentation.api.internal.AttributesExtractorUtil.internalSet;

final class GrpcAttributesExtractor implements AttributesExtractor<GrpcRequest, Status> {
// TODO: This should be added to package io.opentelemetry.semconv.trace.attributes in the next release
public static final String GRPC_METADATA_ATTRIBUTE_VALUE_PREFIX = "rpc.request.metadata";

private final GrpcRpcAttributesGetter getter;
private final List<String> requestMetadataValuesToCapture;

GrpcAttributesExtractor(
GrpcRpcAttributesGetter getter,
List<String> requestMetadataValuesToCapture) {
this.getter = getter;
this.requestMetadataValuesToCapture = requestMetadataValuesToCapture;
}

@Override
public void onStart(
AttributesBuilder attributes, Context parentContext, GrpcRequest grpcRequest) {
// No request attributes
public void onStart(AttributesBuilder attributes, Context parentContext, GrpcRequest grpcRequest) {
// Request attributes captured on request end (onEnd)
}

@Override
Expand All @@ -29,5 +46,15 @@ public void onEnd(
if (status != null) {
attributes.put(SemanticAttributes.RPC_GRPC_STATUS_CODE, status.getCode().value());
}

if (requestMetadataValuesToCapture != null) {
for (String key : requestMetadataValuesToCapture) {
internalSet(
attributes,
AttributeKey.stringArrayKey(GRPC_METADATA_ATTRIBUTE_VALUE_PREFIX + "." + key),
getter.metadataValue(request, key)
);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@

package io.opentelemetry.instrumentation.grpc.v1_6;

import io.grpc.Metadata;
import io.opentelemetry.instrumentation.api.instrumenter.rpc.RpcAttributesGetter;
import javax.annotation.Nullable;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

enum GrpcRpcAttributesGetter implements RpcAttributesGetter<GrpcRequest> {
INSTANCE;
Expand Down Expand Up @@ -37,4 +41,26 @@ public String method(GrpcRequest request) {
}
return fullMethodName.substring(slashIndex + 1);
}

@Nullable
public List<String> metadataValue(GrpcRequest request, String key) {
if (request.getMetadata() == null) {
return null;
}

if (key == null || key.isEmpty()) {
return null;
}

Iterable<String> values = request.getMetadata().getAll(
Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER)
);

if (values == null) {
return null;
}

return StreamSupport.stream(values.spliterator(), false)
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public final class GrpcTelemetryBuilder {
additionalClientExtractors = new ArrayList<>();

private boolean captureExperimentalSpanAttributes;
private List<String> requestMetadataValuesToCapture;


GrpcTelemetryBuilder(OpenTelemetry openTelemetry) {
this.openTelemetry = openTelemetry;
Expand Down Expand Up @@ -115,6 +117,14 @@ public GrpcTelemetryBuilder setCaptureExperimentalSpanAttributes(
return this;
}

/** Sets which metadata request values should be captured as span attributes. */
@CanIgnoreReturnValue
public GrpcTelemetryBuilder setRequestMetadataValuesToCapture(
List<String> requestMetadataValuesToCapture) {
this.requestMetadataValuesToCapture = requestMetadataValuesToCapture;
return this;
}

/** Returns a new {@link GrpcTelemetry} with the settings of this {@link GrpcTelemetryBuilder}. */
public GrpcTelemetry build() {
SpanNameExtractor<GrpcRequest> originalSpanNameExtractor = new GrpcSpanNameExtractor();
Expand All @@ -139,7 +149,9 @@ public GrpcTelemetry build() {
instrumenter ->
instrumenter
.setSpanStatusExtractor(new GrpcSpanStatusExtractor())
.addAttributesExtractor(new GrpcAttributesExtractor())
.addAttributesExtractor(
new GrpcAttributesExtractor(
GrpcRpcAttributesGetter.INSTANCE, requestMetadataValuesToCapture))
.addAttributesExtractors(additionalExtractors));

GrpcNetClientAttributesGetter netClientAttributesGetter = new GrpcNetClientAttributesGetter();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import org.junit.jupiter.api.Test;
Expand All @@ -35,6 +37,7 @@ class GrpcTest extends AbstractGrpcTest {
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();

private static final AttributeKey<String> CUSTOM_KEY = AttributeKey.stringKey("customKey");
private static final String METADATA_ATTRIBUTE_PREFIX = "rpc.request.metadata.";

private static final Metadata.Key<String> CUSTOM_METADATA_KEY =
Metadata.Key.of("customMetadataKey", Metadata.ASCII_STRING_MARSHALLER);
Expand All @@ -56,6 +59,81 @@ protected InstrumentationExtension testing() {
return testing;
}

@Test
void grpcAttributesExtractor() throws Exception {
String metadataKey = "some-key";
AttributeKey<List<String>> attributeKey = AttributeKey.stringArrayKey(METADATA_ATTRIBUTE_PREFIX + metadataKey);
String metadataValue = "some-value";
List<String> metadataValueAsList = Collections.singletonList("some-value");

BindableService greeter =
new GreeterGrpc.GreeterImplBase() {
@Override
public void sayHello(
Helloworld.Request req, StreamObserver<Helloworld.Response> responseObserver) {
Helloworld.Response reply =
Helloworld.Response.newBuilder().setMessage("Hello " + req.getName()).build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
};

GrpcAttributesExtractor grpcAttributesExtractor = new GrpcAttributesExtractor(
GrpcRpcAttributesGetter.INSTANCE,
Collections.singletonList(metadataKey));

Server server =
ServerBuilder.forPort(0)
.addService(greeter)
.intercept(
GrpcTelemetry.builder(testing.getOpenTelemetry())
.addAttributeExtractor(grpcAttributesExtractor)
.build()
.newServerInterceptor())
.build()
.start();

ManagedChannel channel =
createChannel(
ManagedChannelBuilder.forAddress("localhost", server.getPort())
.intercept(
GrpcTelemetry.builder(testing.getOpenTelemetry())
.addAttributeExtractor(grpcAttributesExtractor)
.build()
.newClientInterceptor()));

Metadata extraMetadata = new Metadata();
extraMetadata.put(Metadata.Key.of(metadataKey, Metadata.ASCII_STRING_MARSHALLER), metadataValue);

GreeterGrpc.GreeterBlockingStub client =
GreeterGrpc.newBlockingStub(channel)
.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(extraMetadata));

Helloworld.Response response =
testing()
.runWithSpan(
"parent",
() -> client.sayHello(Helloworld.Request.newBuilder().setName("test").build()));

OpenTelemetryAssertions.assertThat(response.getMessage()).isEqualTo("Hello test");

testing()
.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(),
span ->
span.hasName("example.Greeter/SayHello")
.hasKind(SpanKind.CLIENT)
.hasParent(trace.getSpan(0))
.hasAttribute(attributeKey, metadataValueAsList),
span ->
span.hasName("example.Greeter/SayHello")
.hasKind(SpanKind.SERVER)
.hasParent(trace.getSpan(1))
.hasAttribute(attributeKey, metadataValueAsList)));
}

@Test
void metadataProvided() throws Exception {
BindableService greeter =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public static CommonConfig get() {
private final List<String> clientResponseHeaders;
private final List<String> serverRequestHeaders;
private final List<String> serverResponseHeaders;
private final List<String> grpcRequestMetadata;
private final boolean statementSanitizationEnabled;

CommonConfig(InstrumentationConfig config) {
Expand All @@ -41,17 +42,18 @@ public static CommonConfig get() {
config.getList("otel.instrumentation.http.capture-headers.server.request", emptyList());
serverResponseHeaders =
config.getList("otel.instrumentation.http.capture-headers.server.response", emptyList());
grpcRequestMetadata =
config.getList("otel.instrumentation.grpc.capture-metadata.request", emptyList());
statementSanitizationEnabled =
config.getBoolean("otel.instrumentation.common.db-statement-sanitizer.enabled", true);

}

public Map<String, String> getPeerServiceMapping() {
return peerServiceMapping;
}

public List<String> getClientRequestHeaders() {
return clientRequestHeaders;
}
public List<String> getClientRequestHeaders() { return clientRequestHeaders; }

public List<String> getClientResponseHeaders() {
return clientResponseHeaders;
Expand All @@ -65,6 +67,10 @@ public List<String> getServerResponseHeaders() {
return serverResponseHeaders;
}

public List<String> getGrpcRequestMetadata() {
return grpcRequestMetadata;
}

public boolean isStatementSanitizationEnabled() {
return statementSanitizationEnabled;
}
Expand Down

0 comments on commit c6f544b

Please sign in to comment.