Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,21 @@ afterEvaluate {
}
}

tasks.test {
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
tasks {
withType<Test>().configureEach {
systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false")
}

val testStableSemconv by registering(Test::class) {
testClassesDirs = sourceSets.test.get().output.classesDirs
classpath = sourceSets.test.get().runtimeClasspath
jvmArgs("-Dotel.semconv-stability.opt-in=rpc")
systemProperty("metadataConfig", "otel.semconv-stability.opt-in=rpc")
}

check {
dependsOn(testStableSemconv)
}
}

if (findProperty("denyUnsafe") as Boolean) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@

package io.opentelemetry.javaagent.instrumentation.armeria.grpc.v1_14;

import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableRpcSemconv;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;

import com.linecorp.armeria.client.grpc.GrpcClientBuilder;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.grpc.v1_6.GrpcTelemetry;
import io.opentelemetry.instrumentation.grpc.v1_6.internal.Internal;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.net.URI;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
Expand All @@ -36,8 +40,14 @@ public void transform(TypeTransformer transformer) {
public static class BuildAdvice {

@Advice.OnMethodEnter
public static void onEnter(@Advice.This GrpcClientBuilder builder) {
builder.intercept(GrpcTelemetry.create(GlobalOpenTelemetry.get()).createClientInterceptor());
public static void onEnter(
@Advice.This GrpcClientBuilder builder, @Advice.FieldValue("uri") @Nullable URI uri) {
String target = null;
if (emitStableRpcSemconv() && uri != null) {
target = uri.getAuthority();
}
GrpcTelemetry telemetry = GrpcTelemetry.create(GlobalOpenTelemetry.get());
builder.intercept(Internal.createClientInterceptor(telemetry, target));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@

package io.opentelemetry.javaagent.instrumentation.armeria.grpc.v1_14;

import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitOldRpcSemconv;
import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableRpcSemconv;
import static io.opentelemetry.instrumentation.testing.junit.rpc.SemconvRpcStabilityUtil.maybeStable;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT;
import static io.opentelemetry.semconv.incubating.MessageIncubatingAttributes.MESSAGE_ID;
import static io.opentelemetry.semconv.incubating.MessageIncubatingAttributes.MESSAGE_TYPE;
import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_GRPC_STATUS_CODE;
import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD;
import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_RESPONSE_STATUS_CODE;
import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE;
import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM;
import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -78,10 +82,17 @@ void grpcInstrumentation() {
.hasKind(SpanKind.CLIENT)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(RPC_SYSTEM, "grpc"),
equalTo(RPC_SERVICE, "example.Greeter"),
equalTo(RPC_METHOD, "SayHello"),
equalTo(RPC_GRPC_STATUS_CODE, (long) Status.Code.OK.value()),
equalTo(maybeStable(RPC_SYSTEM), "grpc"),
equalTo(RPC_SERVICE, emitOldRpcSemconv() ? "example.Greeter" : null),
equalTo(
RPC_METHOD,
emitStableRpcSemconv() ? "example.Greeter/SayHello" : "SayHello"),
equalTo(
RPC_GRPC_STATUS_CODE,
emitOldRpcSemconv() ? (long) Status.Code.OK.value() : null),
equalTo(
RPC_RESPONSE_STATUS_CODE,
emitStableRpcSemconv() ? Status.Code.OK.name() : null),
equalTo(SERVER_ADDRESS, "127.0.0.1"),
equalTo(SERVER_PORT, (long) server.httpPort()))
.hasEventsSatisfyingExactly(
Expand All @@ -101,10 +112,17 @@ void grpcInstrumentation() {
.hasKind(SpanKind.SERVER)
.hasParent(trace.getSpan(1))
.hasAttributesSatisfyingExactly(
equalTo(RPC_SYSTEM, "grpc"),
equalTo(RPC_SERVICE, "example.Greeter"),
equalTo(RPC_METHOD, "SayHello"),
equalTo(RPC_GRPC_STATUS_CODE, (long) Status.Code.OK.value()),
equalTo(maybeStable(RPC_SYSTEM), "grpc"),
equalTo(RPC_SERVICE, emitOldRpcSemconv() ? "example.Greeter" : null),
equalTo(
RPC_METHOD,
emitStableRpcSemconv() ? "example.Greeter/SayHello" : "SayHello"),
equalTo(
RPC_GRPC_STATUS_CODE,
emitOldRpcSemconv() ? (long) Status.Code.OK.value() : null),
equalTo(
RPC_RESPONSE_STATUS_CODE,
emitStableRpcSemconv() ? Status.Code.OK.name() : null),
equalTo(SERVER_ADDRESS, "127.0.0.1"),
equalTo(SERVER_PORT, server.httpPort()))
.hasEventsSatisfyingExactly(
Expand Down
40 changes: 30 additions & 10 deletions instrumentation/grpc-1.6/javaagent/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ dependencies {
val collectMetadata = findProperty("collectMetadata")?.toString() ?: "false"

tasks {
test {
withType<Test>().configureEach {
systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean)
// The agent context debug mechanism isn't compatible with the bridge approach which may add a
// gRPC context to the root.
Expand All @@ -56,26 +56,46 @@ tasks {
testClassesDirs = sourceSets.test.get().output.classesDirs
classpath = sourceSets.test.get().runtimeClasspath

// replicated base config from standard test task
systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean)
jvmArgs("-Dotel.javaagent.experimental.thread-propagation-debugger.enabled=false")
jvmArgs("-Dotel.instrumentation.grpc.capture-metadata.client.request=some-client-key")
jvmArgs("-Dotel.instrumentation.grpc.capture-metadata.server.request=some-server-key")
jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true")

// exclude our grpc library instrumentation, the ContextStorageOverride contained within it
// breaks the tests
classpath = classpath.filter {
!it.absolutePath.contains("opentelemetry-grpc-1.6")
}

systemProperty("collectMetadata", collectMetadata)
systemProperty("metadataConfig", "otel.instrumentation.grpc.experimental-span-attributes=true")
jvmArgs("-Dotel.instrumentation.grpc.experimental-span-attributes=true")
}

val testStableSemconv by registering(Test::class) {
testClassesDirs = sourceSets.test.get().output.classesDirs
classpath = sourceSets.test.get().runtimeClasspath

// exclude our grpc library instrumentation, the ContextStorageOverride contained within it
// breaks the tests
classpath = classpath.filter {
!it.absolutePath.contains("opentelemetry-grpc-1.6")
}

jvmArgs("-Dotel.semconv-stability.opt-in=rpc")
systemProperty("metadataConfig", "otel.semconv-stability.opt-in=rpc")
}

val testBothSemconv by registering(Test::class) {
testClassesDirs = sourceSets.test.get().output.classesDirs
classpath = sourceSets.test.get().runtimeClasspath

// exclude our grpc library instrumentation, the ContextStorageOverride contained within it
// breaks the tests
classpath = classpath.filter {
!it.absolutePath.contains("opentelemetry-grpc-1.6")
}

jvmArgs("-Dotel.semconv-stability.opt-in=rpc/dup")
systemProperty("metadataConfig", "otel.semconv-stability.opt-in=rpc/dup")
}

check {
dependsOn(testExperimental)
dependsOn(testExperimental, testStableSemconv, testBothSemconv)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package io.opentelemetry.javaagent.instrumentation.grpc.v1_6;

import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableRpcSemconv;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static io.opentelemetry.javaagent.instrumentation.grpc.v1_6.GrpcSingletons.MANAGED_CHANNEL_BUILDER_INSTRUMENTED;
Expand All @@ -30,7 +31,8 @@ public ElementMatcher<ClassLoader> classLoaderOptimization() {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return extendsClass(named("io.grpc.ManagedChannelBuilder"))
.and(declaresField(named("interceptors")));
.and(declaresField(named("interceptors")))
.and(declaresField(named("target")));
}

@Override
Expand All @@ -46,9 +48,11 @@ public static class AddInterceptorAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void addInterceptor(
@Advice.This ManagedChannelBuilder<?> builder,
@Advice.FieldValue("interceptors") List<ClientInterceptor> interceptors) {
@Advice.FieldValue("interceptors") List<ClientInterceptor> interceptors,
@Advice.FieldValue("target") String target) {
if (!Boolean.TRUE.equals(MANAGED_CHANNEL_BUILDER_INSTRUMENTED.get(builder))) {
interceptors.add(0, GrpcSingletons.CLIENT_INTERCEPTOR);
String effectiveTarget = emitStableRpcSemconv() ? target : null;
interceptors.add(0, GrpcSingletons.createClientInterceptor(effectiveTarget));
MANAGED_CHANNEL_BUILDER_INSTRUMENTED.set(builder, true);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
import io.opentelemetry.instrumentation.api.util.VirtualField;
import io.opentelemetry.instrumentation.grpc.v1_6.GrpcTelemetry;
import io.opentelemetry.instrumentation.grpc.v1_6.internal.ContextStorageBridge;
import io.opentelemetry.instrumentation.grpc.v1_6.internal.Internal;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Nullable;

// Holds singleton references.
public final class GrpcSingletons {
Expand All @@ -31,7 +33,7 @@ public final class GrpcSingletons {
public static final VirtualField<ServerBuilder<?>, Boolean> SERVER_BUILDER_INSTRUMENTED =
VirtualField.find(ServerBuilder.class, Boolean.class);

public static final ClientInterceptor CLIENT_INTERCEPTOR;
private static final GrpcTelemetry TELEMETRY;

public static final ServerInterceptor SERVER_INTERCEPTOR;

Expand Down Expand Up @@ -64,10 +66,14 @@ public final class GrpcSingletons {
.setCapturedServerRequestMetadata(serverRequestMetadata)
.build();

CLIENT_INTERCEPTOR = telemetry.createClientInterceptor();
TELEMETRY = telemetry;
SERVER_INTERCEPTOR = telemetry.createServerInterceptor();
}

public static ClientInterceptor createClientInterceptor(@Nullable String target) {
return Internal.createClientInterceptor(TELEMETRY, target);
}

public static Context.Storage getStorage() {
return STORAGE_REFERENCE.get();
}
Expand Down
2 changes: 1 addition & 1 deletion instrumentation/grpc-1.6/library/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ The instrumentation library provides the implementation of `ClientInterceptor` a
// For client-side, attach the interceptor to your channel builder.
void configureClientInterceptor(OpenTelemetry openTelemetry, NettyChannelBuilder nettyChannelBuilder) {
GrpcTelemetry grpcTelemetry = GrpcTelemetry.create(openTelemetry);
nettyChannelBuilder.intercept(grpcTelemetry.createClientInterceptor());
grpcTelemetry.addClientInterceptor(nettyChannelBuilder);
}

// For server-side, attatch the interceptor to your service.
Expand Down
25 changes: 23 additions & 2 deletions instrumentation/grpc-1.6/library/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,34 @@ dependencies {
}

tasks {
test {
withType<Test>().configureEach {
systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean)
jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true")
// latest dep test occasionally fails because network type is ipv6 instead of the expected ipv4
// and peer address is 0:0:0:0:0:0:0:1 instead of 127.0.0.1
jvmArgs("-Djava.net.preferIPv4Stack=true")
}

test {
jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true")
}

val testStableSemconv by registering(Test::class) {
testClassesDirs = sourceSets.test.get().output.classesDirs
classpath = sourceSets.test.get().runtimeClasspath

jvmArgs("-Dotel.semconv-stability.opt-in=rpc")
}

val testBothSemconv by registering(Test::class) {
testClassesDirs = sourceSets.test.get().output.classesDirs
classpath = sourceSets.test.get().runtimeClasspath

jvmArgs("-Dotel.semconv-stability.opt-in=rpc/dup")
}

check {
dependsOn(testStableSemconv, testBothSemconv)
}
}

if (!(findProperty("testLatestDeps") as Boolean)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@
final class CapturedGrpcMetadataUtil {
private static final String RPC_REQUEST_METADATA_KEY_ATTRIBUTE_PREFIX =
"rpc.grpc.request.metadata.";
private static final String RPC_STABLE_REQUEST_METADATA_KEY_ATTRIBUTE_PREFIX =
"rpc.request.metadata.";
private static final ConcurrentMap<String, AttributeKey<List<String>>> requestKeysCache =
new ConcurrentHashMap<>();
private static final ConcurrentMap<String, AttributeKey<List<String>>> stableRequestKeysCache =
new ConcurrentHashMap<>();

static List<String> lowercase(List<String> names) {
return unmodifiableList(names.stream().map(s -> s.toLowerCase(Locale.ROOT)).collect(toList()));
Expand All @@ -29,9 +33,19 @@ static AttributeKey<List<String>> requestAttributeKey(String metadataKey) {
metadataKey, CapturedGrpcMetadataUtil::createRequestKey);
}

static AttributeKey<List<String>> stableRequestAttributeKey(String metadataKey) {
return stableRequestKeysCache.computeIfAbsent(
metadataKey, CapturedGrpcMetadataUtil::createStableRequestKey);
}

private static AttributeKey<List<String>> createRequestKey(String metadataKey) {
return AttributeKey.stringArrayKey(RPC_REQUEST_METADATA_KEY_ATTRIBUTE_PREFIX + metadataKey);
}

private static AttributeKey<List<String>> createStableRequestKey(String metadataKey) {
return AttributeKey.stringArrayKey(
RPC_STABLE_REQUEST_METADATA_KEY_ATTRIBUTE_PREFIX + metadataKey);
}

private CapturedGrpcMetadataUtil() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@

package io.opentelemetry.instrumentation.grpc.v1_6;

import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitOldRpcSemconv;
import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableRpcSemconv;
import static io.opentelemetry.instrumentation.grpc.v1_6.CapturedGrpcMetadataUtil.lowercase;
import static io.opentelemetry.instrumentation.grpc.v1_6.CapturedGrpcMetadataUtil.requestAttributeKey;
import static io.opentelemetry.instrumentation.grpc.v1_6.CapturedGrpcMetadataUtil.stableRequestAttributeKey;

import io.grpc.Status;
import io.opentelemetry.api.common.AttributeKey;
Expand All @@ -21,6 +24,8 @@ final class GrpcAttributesExtractor implements AttributesExtractor<GrpcRequest,
// copied from RpcIncubatingAttributes
private static final AttributeKey<Long> RPC_GRPC_STATUS_CODE =
AttributeKey.longKey("rpc.grpc.status_code");
private static final AttributeKey<String> RPC_RESPONSE_STATUS_CODE =
AttributeKey.stringKey("rpc.response.status_code");

private final GrpcRpcAttributesGetter getter;
private final List<String> capturedRequestMetadata;
Expand All @@ -44,12 +49,22 @@ public void onEnd(
@Nullable Status status,
@Nullable Throwable error) {
if (status != null) {
attributes.put(RPC_GRPC_STATUS_CODE, status.getCode().value());
if (emitOldRpcSemconv()) {
attributes.put(RPC_GRPC_STATUS_CODE, status.getCode().value());
}
if (emitStableRpcSemconv()) {
attributes.put(RPC_RESPONSE_STATUS_CODE, status.getCode().name());
}
}
for (String key : capturedRequestMetadata) {
List<String> value = getter.metadataValue(request, key);
if (!value.isEmpty()) {
attributes.put(requestAttributeKey(key), value);
if (emitOldRpcSemconv()) {
attributes.put(requestAttributeKey(key), value);
}
if (emitStableRpcSemconv()) {
attributes.put(stableRequestAttributeKey(key), value);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ final class GrpcNetworkServerAttributesGetter
@Nullable
@Override
public String getServerAddress(GrpcRequest grpcRequest) {
return grpcRequest.getLogicalHost();
return grpcRequest.getServerAddress();
}

@Override
public Integer getServerPort(GrpcRequest grpcRequest) {
return grpcRequest.getLogicalPort();
return grpcRequest.getServerPort();
}

@Nullable
Expand Down
Loading
Loading