diff --git a/instrumentation/armeria/armeria-grpc-1.14/javaagent/build.gradle.kts b/instrumentation/armeria/armeria-grpc-1.14/javaagent/build.gradle.kts index 118a3a579133..1b1c7cef52fe 100644 --- a/instrumentation/armeria/armeria-grpc-1.14/javaagent/build.gradle.kts +++ b/instrumentation/armeria/armeria-grpc-1.14/javaagent/build.gradle.kts @@ -59,8 +59,21 @@ afterEvaluate { } } -tasks.test { - systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") +tasks { + withType().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) { diff --git a/instrumentation/armeria/armeria-grpc-1.14/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/armeria/grpc/v1_14/ArmeriaGrpcClientBuilderInstrumentation.java b/instrumentation/armeria/armeria-grpc-1.14/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/armeria/grpc/v1_14/ArmeriaGrpcClientBuilderInstrumentation.java index 288d5f523fed..acf48b5a24bf 100644 --- a/instrumentation/armeria/armeria-grpc-1.14/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/armeria/grpc/v1_14/ArmeriaGrpcClientBuilderInstrumentation.java +++ b/instrumentation/armeria/armeria-grpc-1.14/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/armeria/grpc/v1_14/ArmeriaGrpcClientBuilderInstrumentation.java @@ -5,6 +5,7 @@ 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; @@ -12,8 +13,11 @@ 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; @@ -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)); } } } diff --git a/instrumentation/armeria/armeria-grpc-1.14/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/armeria/grpc/v1_14/ArmeriaGrpcTest.java b/instrumentation/armeria/armeria-grpc-1.14/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/armeria/grpc/v1_14/ArmeriaGrpcTest.java index c5c5394370ef..9fe9a7db9b7d 100644 --- a/instrumentation/armeria/armeria-grpc-1.14/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/armeria/grpc/v1_14/ArmeriaGrpcTest.java +++ b/instrumentation/armeria/armeria-grpc-1.14/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/armeria/grpc/v1_14/ArmeriaGrpcTest.java @@ -5,6 +5,9 @@ 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; @@ -12,6 +15,7 @@ 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; @@ -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( @@ -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( diff --git a/instrumentation/grpc-1.6/javaagent/build.gradle.kts b/instrumentation/grpc-1.6/javaagent/build.gradle.kts index d4eca5080f1d..d3b786eacbd1 100644 --- a/instrumentation/grpc-1.6/javaagent/build.gradle.kts +++ b/instrumentation/grpc-1.6/javaagent/build.gradle.kts @@ -31,7 +31,7 @@ dependencies { val collectMetadata = findProperty("collectMetadata")?.toString() ?: "false" tasks { - test { + withType().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. @@ -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) } } diff --git a/instrumentation/grpc-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grpc/v1_6/GrpcClientBuilderBuildInstrumentation.java b/instrumentation/grpc-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grpc/v1_6/GrpcClientBuilderBuildInstrumentation.java index 42bd46faf66f..643624a87f5b 100644 --- a/instrumentation/grpc-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grpc/v1_6/GrpcClientBuilderBuildInstrumentation.java +++ b/instrumentation/grpc-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grpc/v1_6/GrpcClientBuilderBuildInstrumentation.java @@ -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; @@ -30,7 +31,8 @@ public ElementMatcher classLoaderOptimization() { @Override public ElementMatcher typeMatcher() { return extendsClass(named("io.grpc.ManagedChannelBuilder")) - .and(declaresField(named("interceptors"))); + .and(declaresField(named("interceptors"))) + .and(declaresField(named("target"))); } @Override @@ -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 interceptors) { + @Advice.FieldValue("interceptors") List 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); } } diff --git a/instrumentation/grpc-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grpc/v1_6/GrpcSingletons.java b/instrumentation/grpc-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grpc/v1_6/GrpcSingletons.java index a28f136fa76b..fe73e62be9cb 100644 --- a/instrumentation/grpc-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grpc/v1_6/GrpcSingletons.java +++ b/instrumentation/grpc-1.6/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/grpc/v1_6/GrpcSingletons.java @@ -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 { @@ -31,7 +33,7 @@ public final class GrpcSingletons { public static final VirtualField, 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; @@ -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(); } diff --git a/instrumentation/grpc-1.6/library/README.md b/instrumentation/grpc-1.6/library/README.md index 09674fbb97a5..c07d20d411b1 100644 --- a/instrumentation/grpc-1.6/library/README.md +++ b/instrumentation/grpc-1.6/library/README.md @@ -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. diff --git a/instrumentation/grpc-1.6/library/build.gradle.kts b/instrumentation/grpc-1.6/library/build.gradle.kts index 00ace4566efb..4d981a56fbc6 100644 --- a/instrumentation/grpc-1.6/library/build.gradle.kts +++ b/instrumentation/grpc-1.6/library/build.gradle.kts @@ -17,13 +17,34 @@ dependencies { } tasks { - test { + withType().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)) { diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/CapturedGrpcMetadataUtil.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/CapturedGrpcMetadataUtil.java index 5c39d4c228f9..9cb5f3d7bac8 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/CapturedGrpcMetadataUtil.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/CapturedGrpcMetadataUtil.java @@ -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>> requestKeysCache = new ConcurrentHashMap<>(); + private static final ConcurrentMap>> stableRequestKeysCache = + new ConcurrentHashMap<>(); static List lowercase(List names) { return unmodifiableList(names.stream().map(s -> s.toLowerCase(Locale.ROOT)).collect(toList())); @@ -29,9 +33,19 @@ static AttributeKey> requestAttributeKey(String metadataKey) { metadataKey, CapturedGrpcMetadataUtil::createRequestKey); } + static AttributeKey> stableRequestAttributeKey(String metadataKey) { + return stableRequestKeysCache.computeIfAbsent( + metadataKey, CapturedGrpcMetadataUtil::createStableRequestKey); + } + private static AttributeKey> createRequestKey(String metadataKey) { return AttributeKey.stringArrayKey(RPC_REQUEST_METADATA_KEY_ATTRIBUTE_PREFIX + metadataKey); } + private static AttributeKey> createStableRequestKey(String metadataKey) { + return AttributeKey.stringArrayKey( + RPC_STABLE_REQUEST_METADATA_KEY_ATTRIBUTE_PREFIX + metadataKey); + } + private CapturedGrpcMetadataUtil() {} } diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcAttributesExtractor.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcAttributesExtractor.java index 1e4f6ee406ea..23aea85c86d2 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcAttributesExtractor.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcAttributesExtractor.java @@ -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; @@ -21,6 +24,8 @@ final class GrpcAttributesExtractor implements AttributesExtractor RPC_GRPC_STATUS_CODE = AttributeKey.longKey("rpc.grpc.status_code"); + private static final AttributeKey RPC_RESPONSE_STATUS_CODE = + AttributeKey.stringKey("rpc.response.status_code"); private final GrpcRpcAttributesGetter getter; private final List capturedRequestMetadata; @@ -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 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); + } } } } diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcNetworkServerAttributesGetter.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcNetworkServerAttributesGetter.java index a7f3701c9c60..8c66d0e69059 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcNetworkServerAttributesGetter.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcNetworkServerAttributesGetter.java @@ -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 diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcRequest.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcRequest.java index 926799191bb2..e68eb40b146d 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcRequest.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcRequest.java @@ -7,6 +7,7 @@ import io.grpc.Metadata; import io.grpc.MethodDescriptor; +import io.opentelemetry.instrumentation.grpc.v1_6.internal.ParsedTarget; import java.net.SocketAddress; import javax.annotation.Nullable; @@ -15,40 +16,65 @@ public final class GrpcRequest { private final MethodDescriptor method; @Nullable private volatile Metadata metadata; - - @Nullable private volatile String logicalHost; - private volatile int logicalPort = -1; + @Nullable private final String serverAddress; + @Nullable private final Integer serverPort; @Nullable private volatile SocketAddress peerSocketAddress; private Long requestSize; private Long responseSize; - GrpcRequest( + /** + * Creates a client-side gRPC request. + * + * @param method the gRPC method descriptor + * @param authority the channel authority (host:port) + * @param parsedTarget the pre-parsed gRPC target (from {@link + * io.opentelemetry.instrumentation.grpc.v1_6.internal.GrpcTargetParser#parse}), or {@code + * null} if unavailable + */ + public static GrpcRequest createClientRequest( + MethodDescriptor method, + @Nullable String authority, + @Nullable ParsedTarget parsedTarget) { + if (parsedTarget != null) { + return new GrpcRequest(method, null, null, parsedTarget.getAddress(), parsedTarget.getPort()); + } + return new GrpcRequest( + method, null, null, hostFromAuthority(authority), portFromAuthority(authority)); + } + + /** + * Creates a server-side gRPC request. + * + * @param method the gRPC method descriptor + * @param metadata the request metadata + * @param peerSocketAddress the peer socket address + * @param authority the request authority + */ + public static GrpcRequest createServerRequest( MethodDescriptor method, @Nullable Metadata metadata, @Nullable SocketAddress peerSocketAddress, @Nullable String authority) { + return new GrpcRequest( + method, + metadata, + peerSocketAddress, + hostFromAuthority(authority), + portFromAuthority(authority)); + } + + private GrpcRequest( + MethodDescriptor method, + @Nullable Metadata metadata, + @Nullable SocketAddress peerSocketAddress, + @Nullable String serverAddress, + @Nullable Integer serverPort) { this.method = method; this.metadata = metadata; this.peerSocketAddress = peerSocketAddress; - setLogicalAddress(authority); - } - - private void setLogicalAddress(@Nullable String authority) { - if (authority == null) { - return; - } - int index = authority.indexOf(':'); - if (index == -1) { - logicalHost = authority; - } else { - logicalHost = authority.substring(0, index); - try { - logicalPort = Integer.parseInt(authority.substring(index + 1)); - } catch (NumberFormatException e) { - // ignore - } - } + this.serverAddress = serverAddress; + this.serverPort = serverPort; } public MethodDescriptor getMethod() { @@ -64,13 +90,45 @@ void setMetadata(Metadata metadata) { this.metadata = metadata; } + /** + * Returns the server address. + * + *

When a target string is available (from gRPC channel configuration), the server address is + * extracted per the gRPC Name + * Resolution spec. Otherwise, falls back to the authority (host portion). + */ + @Nullable + public String getServerAddress() { + return serverAddress; + } + + /** + * Returns the server port. + * + *

When a target string is available (from gRPC channel configuration), the server port is + * extracted per the gRPC Name + * Resolution spec. Otherwise, falls back to the authority (port portion). + */ + @Nullable + public Integer getServerPort() { + return serverPort; + } + + /** + * @deprecated Use {@link #getServerAddress()} instead. + */ + @Deprecated @Nullable public String getLogicalHost() { - return logicalHost; + return serverAddress; } + /** + * @deprecated Use {@link #getServerPort()} instead. + */ + @Deprecated public int getLogicalPort() { - return logicalPort; + return serverPort != null ? serverPort : -1; } @Nullable @@ -97,4 +155,32 @@ public Long getResponseSize() { public void setResponseSize(Long responseSize) { this.responseSize = responseSize; } + + @Nullable + private static String hostFromAuthority(@Nullable String authority) { + if (authority == null) { + return null; + } + int index = authority.indexOf(':'); + if (index == -1) { + return authority; + } + return authority.substring(0, index); + } + + @Nullable + private static Integer portFromAuthority(@Nullable String authority) { + if (authority == null) { + return null; + } + int index = authority.indexOf(':'); + if (index == -1) { + return null; + } + try { + return Integer.parseInt(authority.substring(index + 1)); + } catch (NumberFormatException e) { + return null; + } + } } diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcRpcAttributesGetter.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcRpcAttributesGetter.java index 250aac6773d9..99f5d4fba3e1 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcRpcAttributesGetter.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcRpcAttributesGetter.java @@ -46,6 +46,11 @@ public String getMethod(GrpcRequest request) { return fullMethodName.substring(slashIndex + 1); } + @Override + public String getRpcMethod(GrpcRequest request) { + return request.getMethod().getFullMethodName(); + } + @Override @Nullable public Long getRequestSize(GrpcRequest request) { diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTelemetry.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTelemetry.java index d4c97c451144..d1199f53b5ca 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTelemetry.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTelemetry.java @@ -6,15 +6,50 @@ package io.opentelemetry.instrumentation.grpc.v1_6; import io.grpc.ClientInterceptor; +import io.grpc.ManagedChannelBuilder; import io.grpc.ServerInterceptor; import io.grpc.Status; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.grpc.v1_6.internal.Internal; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.Nullable; /** Entrypoint for instrumenting gRPC servers or clients. */ public final class GrpcTelemetry { + private static final Logger logger = Logger.getLogger(GrpcTelemetry.class.getName()); + + // Reflective access to InternalManagedChannelBuilder.interceptWithTarget (available since gRPC + // 1.64.0). Uses the public static accessor instead of the protected method on + // ManagedChannelBuilder to avoid needing setAccessible(true). + @Nullable private static final Method interceptWithTargetMethod; + @Nullable private static final Class interceptorFactoryClass; + + static { + Method method = null; + Class factoryClass = null; + try { + Class internalBuilder = Class.forName("io.grpc.InternalManagedChannelBuilder"); + factoryClass = + Class.forName("io.grpc.InternalManagedChannelBuilder$InternalInterceptorFactory"); + method = + internalBuilder.getMethod( + "interceptWithTarget", ManagedChannelBuilder.class, factoryClass); + } catch (ClassNotFoundException | NoSuchMethodException e) { + // gRPC version < 1.64.0, interceptWithTarget not available + } + interceptWithTargetMethod = method; + interceptorFactoryClass = factoryClass; + + Internal.setClientInterceptorFactory( + (telemetry, target) -> telemetry.newTracingClientInterceptor(target)); + } + /** Returns a new {@link GrpcTelemetry} configured with the given {@link OpenTelemetry}. */ public static GrpcTelemetry create(OpenTelemetry openTelemetry) { return builder(openTelemetry).build(); @@ -44,13 +79,53 @@ public static GrpcTelemetryBuilder builder(OpenTelemetry openTelemetry) { this.emitMessageEvents = emitMessageEvents; } + /** + * Configures the given {@link ManagedChannelBuilder} with OpenTelemetry tracing instrumentation. + * + *

On gRPC 1.64.0+, this method automatically captures the channel's target string for + * populating {@code server.address} and {@code server.port} attributes. On older gRPC versions, + * it falls back to using the channel's authority. + * + *

This is the recommended way to instrument a gRPC channel, instead of calling {@link + * #createClientInterceptor()} and adding the interceptor manually. + * + * @param builder the channel builder to configure + */ + @SuppressWarnings("unchecked") + public void addClientInterceptor(ManagedChannelBuilder builder) { + if (interceptWithTargetMethod != null && interceptorFactoryClass != null) { + try { + Object factory = + Proxy.newProxyInstance( + ManagedChannelBuilder.class.getClassLoader(), + new Class[] {interceptorFactoryClass}, + (proxy, method, args) -> { + if ("newInterceptor".equals(method.getName())) { + String target = (String) args[0]; + return newTracingClientInterceptor(target); + } + return method.invoke(builder, args); + }); + interceptWithTargetMethod.invoke(null, builder, factory); + return; + } catch (Exception e) { + logger.log(Level.FINE, "Failed to use interceptWithTarget, falling back", e); + } + } + + // Fallback for gRPC < 1.64.0: add interceptor without target info + builder.intercept(newTracingClientInterceptor(null)); + } + /** * Returns a new {@link ClientInterceptor} for use with methods like {@link * io.grpc.ManagedChannelBuilder#intercept(ClientInterceptor...)}. + * + * @deprecated Use {@link #addClientInterceptor(ManagedChannelBuilder)} instead. */ + @Deprecated public ClientInterceptor createClientInterceptor() { - return new TracingClientInterceptor( - clientInstrumenter, propagators, captureExperimentalSpanAttributes, emitMessageEvents); + return newTracingClientInterceptor(null); } /** @@ -61,4 +136,13 @@ public ServerInterceptor createServerInterceptor() { return new TracingServerInterceptor( serverInstrumenter, captureExperimentalSpanAttributes, emitMessageEvents); } + + private TracingClientInterceptor newTracingClientInterceptor(@Nullable String target) { + return new TracingClientInterceptor( + clientInstrumenter, + propagators, + captureExperimentalSpanAttributes, + emitMessageEvents, + target); + } } diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTelemetryBuilder.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTelemetryBuilder.java index 703a93496a52..a1eee0767fbf 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTelemetryBuilder.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTelemetryBuilder.java @@ -12,6 +12,7 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcClientAttributesExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcClientMetrics; +import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcMetricsContextCustomizers; import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcServerAttributesExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcServerMetrics; import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcSizeAttributesExtractor; @@ -151,6 +152,7 @@ public GrpcTelemetryBuilder setCapturedServerRequestMetadata( } /** Returns a new {@link GrpcTelemetry} with the settings of this {@link GrpcTelemetryBuilder}. */ + @SuppressWarnings("deprecation") // RpcMetricsContextCustomizers is deprecated for removal in 3.0 public GrpcTelemetry build() { SpanNameExtractor originalSpanNameExtractor = new GrpcSpanNameExtractor(); SpanNameExtractor clientSpanNameExtractor = @@ -179,7 +181,9 @@ public GrpcTelemetry build() { .addAttributesExtractor( new GrpcAttributesExtractor( GrpcRpcAttributesGetter.INSTANCE, capturedClientRequestMetadata)) - .addOperationMetrics(RpcClientMetrics.get()); + .addOperationMetrics(RpcClientMetrics.get()) + .addContextCustomizer( + RpcMetricsContextCustomizers.dualEmitContextCustomizer(rpcAttributesGetter)); Experimental.addOperationListenerAttributesExtractor( clientInstrumenterBuilder, RpcSizeAttributesExtractor.create(rpcAttributesGetter)); serverInstrumenterBuilder @@ -192,7 +196,9 @@ public GrpcTelemetry build() { new GrpcAttributesExtractor( GrpcRpcAttributesGetter.INSTANCE, capturedServerRequestMetadata)) .addAttributesExtractors(additionalServerExtractors) - .addOperationMetrics(RpcServerMetrics.get()); + .addOperationMetrics(RpcServerMetrics.get()) + .addContextCustomizer( + RpcMetricsContextCustomizers.dualEmitContextCustomizer(rpcAttributesGetter)); Experimental.addOperationListenerAttributesExtractor( serverInstrumenterBuilder, RpcSizeAttributesExtractor.create(rpcAttributesGetter)); diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingClientInterceptor.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingClientInterceptor.java index 7bce2c0c915b..06dff52b8d74 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingClientInterceptor.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingClientInterceptor.java @@ -22,7 +22,10 @@ import io.opentelemetry.context.Scope; import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.grpc.v1_6.internal.GrpcTargetParser; +import io.opentelemetry.instrumentation.grpc.v1_6.internal.ParsedTarget; import java.util.concurrent.atomic.AtomicLongFieldUpdater; +import javax.annotation.Nullable; final class TracingClientInterceptor implements ClientInterceptor { @@ -49,22 +52,25 @@ final class TracingClientInterceptor implements ClientInterceptor { private final ContextPropagators propagators; private final boolean captureExperimentalSpanAttributes; private final boolean emitMessageEvents; + @Nullable private final ParsedTarget parsedTarget; TracingClientInterceptor( Instrumenter instrumenter, ContextPropagators propagators, boolean captureExperimentalSpanAttributes, - boolean emitMessageEvents) { + boolean emitMessageEvents, + @Nullable String target) { this.instrumenter = instrumenter; this.propagators = propagators; this.captureExperimentalSpanAttributes = captureExperimentalSpanAttributes; this.emitMessageEvents = emitMessageEvents; + this.parsedTarget = GrpcTargetParser.parse(target); } @Override public ClientCall interceptCall( MethodDescriptor method, CallOptions callOptions, Channel next) { - GrpcRequest request = new GrpcRequest(method, null, null, next.authority()); + GrpcRequest request = GrpcRequest.createClientRequest(method, next.authority(), parsedTarget); Context parentContext = Context.current(); if (!instrumenter.shouldStart(parentContext, request)) { return next.newCall(method, callOptions); diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingServerInterceptor.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingServerInterceptor.java index 5440d9ba34db..2ffec58f0b81 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingServerInterceptor.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingServerInterceptor.java @@ -71,7 +71,7 @@ public ServerCall.Listener interceptCall( authority = GrpcAuthorityStorage.getAuthority(call); } GrpcRequest request = - new GrpcRequest( + GrpcRequest.createServerRequest( call.getMethodDescriptor(), headers, call.getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR), diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcClientNetworkAttributesGetter.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcClientNetworkAttributesGetter.java index 1a8ce22d8eff..50a150e7e2f1 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcClientNetworkAttributesGetter.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcClientNetworkAttributesGetter.java @@ -23,12 +23,12 @@ public final class GrpcClientNetworkAttributesGetter @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(); } @Override diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcTargetParser.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcTargetParser.java new file mode 100644 index 000000000000..12cd5480336c --- /dev/null +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcTargetParser.java @@ -0,0 +1,147 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.grpc.v1_6.internal; + +import javax.annotation.Nullable; + +/** + * Parses gRPC target strings into server address and port per the gRPC Name Resolution spec and semantic conventions. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class GrpcTargetParser { + + private GrpcTargetParser() {} + + @Nullable + public static ParsedTarget parse(@Nullable String target) { + if (target == null || target.isEmpty()) { + return null; + } + + int schemeEnd = target.indexOf("://"); + if (schemeEnd == -1) { + // Check for single-colon scheme like "dns:endpoint" or "unix:/path" + int colonIndex = target.indexOf(':'); + if (colonIndex == -1) { + // No scheme, no port — just a host name + return new ParsedTarget(target, null); + } + + String potentialScheme = target.substring(0, colonIndex); + if (isKnownScheme(potentialScheme)) { + return parseSingleColonScheme(potentialScheme, target.substring(colonIndex + 1)); + } + + // No known scheme — treat as "host:port" + return parseHostPort(target); + } + + String scheme = target.substring(0, schemeEnd); + String rest = target.substring(schemeEnd + 3); // after "://" + + if ("dns".equals(scheme)) { + return parseDnsScheme(rest); + } + + if ("unix".equals(scheme) || "unix-abstract".equals(scheme)) { + // unix://authority/path — the path (after authority) is the address + int slashIndex = rest.indexOf('/'); + if (slashIndex != -1) { + return new ParsedTarget(rest.substring(slashIndex), null); + } + return new ParsedTarget(rest, null); + } + + // Unknown scheme with "://" — use full target string as address, no port + return new ParsedTarget(target, null); + } + + private static ParsedTarget parseSingleColonScheme(String scheme, String rest) { + if ("dns".equals(scheme)) { + return parseHostPort(rest); + } + + if ("unix".equals(scheme) || "unix-abstract".equals(scheme)) { + return new ParsedTarget(rest, null); + } + + // ipv4:, ipv6:, or other — full target as address + return new ParsedTarget(scheme + ":" + rest, null); + } + + private static ParsedTarget parseDnsScheme(String rest) { + int slashIndex = rest.indexOf('/'); + String endpoint; + if (slashIndex != -1) { + endpoint = rest.substring(slashIndex + 1); + } else { + endpoint = rest; + } + return parseHostPort(endpoint); + } + + private static ParsedTarget parseHostPort(String hostPort) { + if (hostPort.isEmpty()) { + return new ParsedTarget(hostPort, null); + } + + // Handle IPv6 in brackets: [::1]:8080 + if (hostPort.startsWith("[")) { + int closeBracket = hostPort.indexOf(']'); + if (closeBracket != -1) { + String host = hostPort.substring(1, closeBracket); + if (closeBracket + 1 < hostPort.length() && hostPort.charAt(closeBracket + 1) == ':') { + Integer port = parsePort(hostPort.substring(closeBracket + 2)); + return new ParsedTarget(host, port); + } + return new ParsedTarget(host, null); + } + } + + int lastColon = hostPort.lastIndexOf(':'); + if (lastColon == -1) { + return new ParsedTarget(hostPort, null); + } + + // Multiple colons — likely bare IPv6, use as-is + int firstColon = hostPort.indexOf(':'); + if (firstColon != lastColon) { + return new ParsedTarget(hostPort, null); + } + + String host = hostPort.substring(0, lastColon); + Integer port = parsePort(hostPort.substring(lastColon + 1)); + if (port != null) { + return new ParsedTarget(host, port); + } + return new ParsedTarget(hostPort, null); + } + + @Nullable + private static Integer parsePort(String portStr) { + try { + int port = Integer.parseInt(portStr); + if (port >= 0 && port <= 65535) { + return port; + } + } catch (NumberFormatException e) { + // ignore + } + return null; + } + + private static boolean isKnownScheme(String scheme) { + return "dns".equals(scheme) + || "unix".equals(scheme) + || "unix-abstract".equals(scheme) + || "ipv4".equals(scheme) + || "ipv6".equals(scheme); + } +} diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/Internal.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/Internal.java new file mode 100644 index 000000000000..61a30d4ba110 --- /dev/null +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/Internal.java @@ -0,0 +1,33 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.grpc.v1_6.internal; + +import io.grpc.ClientInterceptor; +import io.opentelemetry.instrumentation.grpc.v1_6.GrpcTelemetry; +import java.util.function.BiFunction; +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. + */ +public final class Internal { + + private static volatile BiFunction + clientInterceptorFactory; + + public static void setClientInterceptorFactory( + BiFunction factory) { + clientInterceptorFactory = factory; + } + + public static ClientInterceptor createClientInterceptor( + GrpcTelemetry telemetry, @Nullable String target) { + return clientInterceptorFactory.apply(telemetry, target); + } + + private Internal() {} +} diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/ParsedTarget.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/ParsedTarget.java new file mode 100644 index 000000000000..7540aef5ada9 --- /dev/null +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/ParsedTarget.java @@ -0,0 +1,34 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.grpc.v1_6.internal; + +import javax.annotation.Nullable; + +/** + * Holds the parsed server address and port from a gRPC target string. + * + *

This class is internal and is hence not for public use. Its APIs are unstable and can change + * at any time. + */ +public final class ParsedTarget { + + private final String address; + @Nullable private final Integer port; + + ParsedTarget(String address, @Nullable Integer port) { + this.address = address; + this.port = port; + } + + public String getAddress() { + return address; + } + + @Nullable + public Integer getPort() { + return port; + } +} diff --git a/instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcStreamingTest.java b/instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcStreamingTest.java index f7323d13e043..2f0bca5e6bba 100644 --- a/instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcStreamingTest.java +++ b/instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcStreamingTest.java @@ -24,8 +24,9 @@ protected ServerBuilder configureServer(ServerBuilder server) { @Override protected ManagedChannelBuilder configureClient(ManagedChannelBuilder client) { - return client.intercept( - GrpcTelemetry.create(testing.getOpenTelemetry()).createClientInterceptor()); + GrpcTelemetry grpcTelemetry = GrpcTelemetry.create(testing.getOpenTelemetry()); + grpcTelemetry.addClientInterceptor(client); + return client; } @Override diff --git a/instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTest.java b/instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTest.java index d24b5c170c02..a521b3a21ac1 100644 --- a/instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTest.java +++ b/instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTest.java @@ -53,11 +53,11 @@ protected ServerBuilder configureServer(ServerBuilder server) { @Override protected ManagedChannelBuilder configureClient(ManagedChannelBuilder client) { - return client.intercept( - GrpcTelemetry.builder(testing.getOpenTelemetry()) - .setCapturedClientRequestMetadata(singletonList(CLIENT_REQUEST_METADATA_KEY)) - .build() - .createClientInterceptor()); + GrpcTelemetry.builder(testing.getOpenTelemetry()) + .setCapturedClientRequestMetadata(singletonList(CLIENT_REQUEST_METADATA_KEY)) + .build() + .addClientInterceptor(client); + return client; } @Override @@ -96,16 +96,14 @@ public void sayHello( .build() .start(); - ManagedChannel channel = - createChannel( - ManagedChannelBuilder.forAddress("localhost", server.getPort()) - .intercept( - GrpcTelemetry.builder(testing.getOpenTelemetry()) - .addAttributesExtractor(new CustomAttributesExtractor()) - .addClientAttributeExtractor( - new CustomAttributesExtractorV2("clientSideValue")) - .build() - .createClientInterceptor())); + ManagedChannelBuilder channelBuilder = + ManagedChannelBuilder.forAddress("localhost", server.getPort()); + GrpcTelemetry.builder(testing.getOpenTelemetry()) + .addAttributesExtractor(new CustomAttributesExtractor()) + .addClientAttributeExtractor(new CustomAttributesExtractorV2("clientSideValue")) + .build() + .addClientInterceptor(channelBuilder); + ManagedChannel channel = createChannel(channelBuilder); closer.add(() -> channel.shutdownNow().awaitTermination(10, SECONDS)); closer.add(() -> server.shutdownNow().awaitTermination()); diff --git a/instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcTargetParserTest.java b/instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcTargetParserTest.java new file mode 100644 index 000000000000..5e80c948a38d --- /dev/null +++ b/instrumentation/grpc-1.6/library/src/test/java/io/opentelemetry/instrumentation/grpc/v1_6/internal/GrpcTargetParserTest.java @@ -0,0 +1,70 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.grpc.v1_6.internal; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullAndEmptySource; + +class GrpcTargetParserTest { + + @ParameterizedTest + @MethodSource("targetProvider") + void parse(String target, String expectedAddress, Integer expectedPort) { + ParsedTarget result = GrpcTargetParser.parse(target); + + assertThat(result).isNotNull(); + assertThat(result.getAddress()).isEqualTo(expectedAddress); + assertThat(result.getPort()).isEqualTo(expectedPort); + } + + static Stream targetProvider() { + return Stream.of( + // dns:/// scheme (triple slash) + Arguments.of("dns:///myhost", "myhost", null), + Arguments.of("dns:///myhost:8080", "myhost", 8080), + + // dns: scheme (single colon) + Arguments.of("dns:myhost", "myhost", null), + Arguments.of("dns:myhost:8080", "myhost", 8080), + + // bare host:port (no scheme) + Arguments.of("myhost", "myhost", null), + Arguments.of("myhost:8080", "myhost", 8080), + Arguments.of("localhost:443", "localhost", 443), + + // unix schemes + Arguments.of("unix:///var/run/grpc.sock", "/var/run/grpc.sock", null), + Arguments.of("unix:/var/run/grpc.sock", "/var/run/grpc.sock", null), + Arguments.of("unix-abstract:name", "name", null), + + // ipv4 scheme + Arguments.of("ipv4:192.168.0.1:8080", "ipv4:192.168.0.1:8080", null), + + // ipv6 scheme + Arguments.of("ipv6:[::1]:8080", "ipv6:[::1]:8080", null), + + // IPv6 in brackets (bare) + Arguments.of("[::1]:8080", "::1", 8080), + Arguments.of("[::1]", "::1", null), + + // bare IPv6 (no brackets) — treated as host with no port + Arguments.of("::1", "::1", null), + + // unknown scheme with :// + Arguments.of("xds:///myservice", "xds:///myservice", null)); + } + + @ParameterizedTest + @NullAndEmptySource + void parseNullOrEmpty(String target) { + assertThat(GrpcTargetParser.parse(target)).isNull(); + } +} diff --git a/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/AbstractGrpcStreamingTest.java b/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/AbstractGrpcStreamingTest.java index 972f8353800b..d828ebb737fe 100644 --- a/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/AbstractGrpcStreamingTest.java +++ b/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/AbstractGrpcStreamingTest.java @@ -5,6 +5,8 @@ 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.AbstractGrpcTest.addExtraClientAttributes; import static io.opentelemetry.instrumentation.grpc.v1_6.ExperimentalTestHelper.GRPC_RECEIVED_MESSAGE_COUNT; import static io.opentelemetry.instrumentation.grpc.v1_6.ExperimentalTestHelper.GRPC_SENT_MESSAGE_COUNT; @@ -21,8 +23,10 @@ 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 io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM_NAME; import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.stream.Collectors.toList; @@ -243,10 +247,23 @@ public void onCompleted() { experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_METHOD, "Conversation"), - equalTo(RPC_GRPC_STATUS_CODE, (long) Status.Code.OK.value()), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SERVICE, + emitOldRpcSemconv() ? "example.Greeter" : null), + equalTo( + RPC_METHOD, + emitStableRpcSemconv() + ? "example.Greeter/Conversation" + : "Conversation"), + 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, "localhost"), equalTo(SERVER_PORT, (long) server.getPort()))) .satisfies( @@ -263,10 +280,21 @@ public void onCompleted() { v -> assertThat(v).isGreaterThan(0)), experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_METHOD, "Conversation"), - equalTo(RPC_GRPC_STATUS_CODE, (long) Status.Code.OK.value()), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo(RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SERVICE, emitOldRpcSemconv() ? "example.Greeter" : null), + equalTo( + RPC_METHOD, + emitStableRpcSemconv() + ? "example.Greeter/Conversation" + : "Conversation"), + 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, "localhost"), equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), @@ -285,49 +313,100 @@ public void onCompleted() { span.hasName("clientOnNext") .hasKind(SpanKind.INTERNAL) .hasParent(trace.getSpan(0)))); - testing() - .waitAndAssertMetrics( - "io.opentelemetry.grpc-1.6", - "rpc.server.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfying( - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(RPC_METHOD, "Conversation"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_SYSTEM, "grpc"), - equalTo( - RPC_GRPC_STATUS_CODE, - (long) Status.Code.OK.value())))))); - testing() - .waitAndAssertMetrics( - "io.opentelemetry.grpc-1.6", - "rpc.client.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfying( - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, server.getPort()), - equalTo(RPC_METHOD, "Conversation"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_SYSTEM, "grpc"), - equalTo( - RPC_GRPC_STATUS_CODE, - (long) Status.Code.OK.value())))))); + if (emitOldRpcSemconv()) { + testing() + .waitAndAssertMetrics( + "io.opentelemetry.grpc-1.6", + "rpc.server.duration", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point.hasAttributesSatisfying( + equalTo(SERVER_ADDRESS, "localhost"), + equalTo(RPC_METHOD, "Conversation"), + equalTo(RPC_SERVICE, "example.Greeter"), + equalTo(RPC_SYSTEM, "grpc"), + equalTo( + RPC_GRPC_STATUS_CODE, + (long) Status.Code.OK.value())))))); + } + if (emitStableRpcSemconv()) { + testing() + .waitAndAssertMetrics( + "io.opentelemetry.grpc-1.6", + "rpc.server.call.duration", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("s") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point.hasAttributesSatisfying( + equalTo(RPC_SYSTEM_NAME, "grpc"), + equalTo(SERVER_ADDRESS, "localhost"), + equalTo( + RPC_METHOD, "example.Greeter/Conversation"), + equalTo( + RPC_RESPONSE_STATUS_CODE, + Status.Code.OK.name())))))); + } + if (emitOldRpcSemconv()) { + testing() + .waitAndAssertMetrics( + "io.opentelemetry.grpc-1.6", + "rpc.client.duration", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point.hasAttributesSatisfying( + equalTo(SERVER_ADDRESS, "localhost"), + equalTo(SERVER_PORT, server.getPort()), + equalTo(RPC_METHOD, "Conversation"), + equalTo(RPC_SERVICE, "example.Greeter"), + equalTo(RPC_SYSTEM, "grpc"), + equalTo( + RPC_GRPC_STATUS_CODE, + (long) Status.Code.OK.value())))))); + } + if (emitStableRpcSemconv()) { + testing() + .waitAndAssertMetrics( + "io.opentelemetry.grpc-1.6", + "rpc.client.call.duration", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("s") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point.hasAttributesSatisfying( + equalTo(RPC_SYSTEM_NAME, "grpc"), + equalTo(SERVER_ADDRESS, "localhost"), + equalTo(SERVER_PORT, server.getPort()), + equalTo( + RPC_METHOD, "example.Greeter/Conversation"), + equalTo( + RPC_RESPONSE_STATUS_CODE, + Status.Code.OK.name())))))); + } } @Test diff --git a/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/AbstractGrpcTest.java b/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/AbstractGrpcTest.java index 32aec3bc7bca..1b01a27424c9 100644 --- a/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/AbstractGrpcTest.java +++ b/instrumentation/grpc-1.6/testing/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/AbstractGrpcTest.java @@ -5,12 +5,15 @@ 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.ExperimentalTestHelper.GRPC_RECEIVED_MESSAGE_COUNT; import static io.opentelemetry.instrumentation.grpc.v1_6.ExperimentalTestHelper.GRPC_SENT_MESSAGE_COUNT; import static io.opentelemetry.instrumentation.grpc.v1_6.ExperimentalTestHelper.experimentalSatisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE; import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_ADDRESS; import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_PORT; import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_TYPE; @@ -20,8 +23,10 @@ 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 io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM_NAME; import static java.util.Collections.singletonList; import static java.util.concurrent.TimeUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -151,10 +156,23 @@ public void sayHello( experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_GRPC_STATUS_CODE, (long) Status.Code.OK.value()), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + 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, "localhost"), equalTo(SERVER_PORT, (long) server.getPort()))) .hasEventsSatisfyingExactly( @@ -179,10 +197,21 @@ public void sayHello( v -> assertThat(v).isGreaterThan(0)), experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_GRPC_STATUS_CODE, (long) Status.Code.OK.value()), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo(RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + 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, "localhost"), equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), @@ -269,10 +298,23 @@ public void sayHello( experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_GRPC_STATUS_CODE, (long) Status.Code.OK.value()), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + 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, "localhost"), equalTo(SERVER_PORT, (long) server.getPort()))) .hasEventsSatisfyingExactly( @@ -297,10 +339,21 @@ public void sayHello( v -> assertThat(v).isGreaterThan(0)), experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_GRPC_STATUS_CODE, (long) Status.Code.OK.value()), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo(RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + 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, "localhost"), equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), @@ -399,10 +452,23 @@ public void onCompleted() { experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_GRPC_STATUS_CODE, (long) Status.Code.OK.value()), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + 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, "localhost"), equalTo(SERVER_PORT, (long) server.getPort()))) .hasEventsSatisfyingExactly( @@ -427,10 +493,21 @@ public void onCompleted() { v -> assertThat(v).isGreaterThan(0)), experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_GRPC_STATUS_CODE, (long) Status.Code.OK.value()), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo(RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + 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, "localhost"), equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), @@ -502,10 +579,25 @@ public void sayHello( experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_GRPC_STATUS_CODE, (long) status.getCode().value()), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SERVICE, + emitOldRpcSemconv() ? "example.Greeter" : null), + equalTo( + RPC_METHOD, + emitStableRpcSemconv() + ? "example.Greeter/SayHello" + : "SayHello"), + equalTo( + RPC_GRPC_STATUS_CODE, + emitOldRpcSemconv() + ? (long) status.getCode().value() + : null), + equalTo( + RPC_RESPONSE_STATUS_CODE, + emitStableRpcSemconv() ? status.getCode().name() : null), equalTo(SERVER_ADDRESS, "localhost"), equalTo(SERVER_PORT, (long) server.getPort()))) .hasEventsSatisfyingExactly( @@ -526,10 +618,26 @@ public void sayHello( v -> assertThat(v).isGreaterThan(0)), experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isEqualTo(0)), - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_GRPC_STATUS_CODE, (long) status.getCode().value()), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo(RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SERVICE, emitOldRpcSemconv() ? "example.Greeter" : null), + equalTo( + RPC_METHOD, + emitStableRpcSemconv() + ? "example.Greeter/SayHello" + : "SayHello"), + equalTo( + ERROR_TYPE, + emitStableRpcSemconv() && status.getCause() != null + ? status.getCause().getClass().getName() + : null), + equalTo( + RPC_GRPC_STATUS_CODE, + emitOldRpcSemconv() ? (long) status.getCode().value() : null), + equalTo( + RPC_RESPONSE_STATUS_CODE, + emitStableRpcSemconv() ? status.getCode().name() : null), equalTo(SERVER_ADDRESS, "localhost"), equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), @@ -606,12 +714,27 @@ public void sayHello( experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_METHOD, "SayHello"), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SERVICE, + emitOldRpcSemconv() ? "example.Greeter" : null), + equalTo( + RPC_METHOD, + emitStableRpcSemconv() + ? "example.Greeter/SayHello" + : "SayHello"), equalTo( RPC_GRPC_STATUS_CODE, - (long) Status.UNKNOWN.getCode().value()), + emitOldRpcSemconv() + ? (long) Status.UNKNOWN.getCode().value() + : null), + equalTo( + RPC_RESPONSE_STATUS_CODE, + emitStableRpcSemconv() + ? Status.UNKNOWN.getCode().name() + : null), equalTo(SERVER_ADDRESS, "localhost"), equalTo(SERVER_PORT, (long) server.getPort()))) .hasEventsSatisfyingExactly( @@ -627,10 +750,28 @@ public void sayHello( .hasParent(trace.getSpan(0)) .hasStatus(StatusData.error()) .hasAttributesSatisfyingExactly( - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_GRPC_STATUS_CODE, (long) Status.Code.UNKNOWN.value()), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo(RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SERVICE, emitOldRpcSemconv() ? "example.Greeter" : null), + equalTo( + RPC_METHOD, + emitStableRpcSemconv() + ? "example.Greeter/SayHello" + : "SayHello"), + equalTo( + ERROR_TYPE, + emitStableRpcSemconv() + ? StatusRuntimeException.class.getName() + : null), + equalTo( + RPC_GRPC_STATUS_CODE, + emitOldRpcSemconv() + ? (long) Status.Code.UNKNOWN.value() + : null), + equalTo( + RPC_RESPONSE_STATUS_CODE, + emitStableRpcSemconv() ? Status.Code.UNKNOWN.name() : null), equalTo(SERVER_ADDRESS, "localhost"), equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), @@ -803,10 +944,23 @@ public void onCompleted() { experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_GRPC_STATUS_CODE, (long) Status.Code.OK.value()), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + 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, "localhost"), equalTo(SERVER_PORT, (long) server.getPort()))) .hasEventsSatisfyingExactly( @@ -831,10 +985,21 @@ public void onCompleted() { v -> assertThat(v).isGreaterThan(0)), experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_GRPC_STATUS_CODE, (long) Status.Code.OK.value()), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo(RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + 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, "localhost"), equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), @@ -927,11 +1092,32 @@ public void onCompleted() { experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_METHOD, "SayMultipleHello"), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SERVICE, + emitOldRpcSemconv() ? "example.Greeter" : null), + equalTo( + RPC_METHOD, + emitStableRpcSemconv() + ? "example.Greeter/SayMultipleHello" + : "SayMultipleHello"), equalTo( - RPC_GRPC_STATUS_CODE, (long) Status.Code.CANCELLED.value()), + ERROR_TYPE, + emitStableRpcSemconv() + ? thrown.getClass().getName() + : null), + equalTo( + RPC_GRPC_STATUS_CODE, + emitOldRpcSemconv() + ? (long) Status.Code.CANCELLED.value() + : null), + equalTo( + RPC_RESPONSE_STATUS_CODE, + emitStableRpcSemconv() + ? Status.Code.CANCELLED.name() + : null), equalTo(SERVER_ADDRESS, "localhost"), equalTo(SERVER_PORT, (long) server.getPort()))) .hasEventsSatisfying( @@ -958,10 +1144,23 @@ public void onCompleted() { v -> assertThat(v).isGreaterThan(0)), experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_METHOD, "SayMultipleHello"), - equalTo(RPC_GRPC_STATUS_CODE, (long) Status.Code.CANCELLED.value()), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo(RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SERVICE, emitOldRpcSemconv() ? "example.Greeter" : null), + equalTo( + RPC_METHOD, + emitStableRpcSemconv() + ? "example.Greeter/SayMultipleHello" + : "SayMultipleHello"), + equalTo( + RPC_GRPC_STATUS_CODE, + emitOldRpcSemconv() + ? (long) Status.Code.CANCELLED.value() + : null), + equalTo( + RPC_RESPONSE_STATUS_CODE, + emitStableRpcSemconv() ? Status.Code.CANCELLED.name() : null), equalTo(SERVER_ADDRESS, "localhost"), equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), @@ -1049,11 +1248,25 @@ public void onCompleted() { experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - equalTo(RPC_SYSTEM, "grpc"), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), equalTo( - RPC_SERVICE, "grpc.reflection.v1alpha.ServerReflection"), - equalTo(RPC_METHOD, "ServerReflectionInfo"), - equalTo(RPC_GRPC_STATUS_CODE, (long) Status.Code.OK.value()), + RPC_SERVICE, + emitOldRpcSemconv() + ? "grpc.reflection.v1alpha.ServerReflection" + : null), + equalTo( + RPC_METHOD, + emitStableRpcSemconv() + ? "grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo" + : "ServerReflectionInfo"), + 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, "localhost"), equalTo(SERVER_PORT, (long) server.getPort()))) .hasEventsSatisfyingExactly( @@ -1079,10 +1292,24 @@ public void onCompleted() { v -> assertThat(v).isGreaterThan(0)), experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "grpc.reflection.v1alpha.ServerReflection"), - equalTo(RPC_METHOD, "ServerReflectionInfo"), - equalTo(RPC_GRPC_STATUS_CODE, (long) Status.Code.OK.value()), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo(RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SERVICE, + emitOldRpcSemconv() + ? "grpc.reflection.v1alpha.ServerReflection" + : null), + equalTo( + RPC_METHOD, + emitStableRpcSemconv() + ? "grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo" + : "ServerReflectionInfo"), + 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, "localhost"), equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), @@ -1153,10 +1380,23 @@ public void sayHello( experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_GRPC_STATUS_CODE, (long) Status.Code.OK.value()), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo( + RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + 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, "localhost"), equalTo(SERVER_PORT, (long) server.getPort()))) .hasEventsSatisfyingExactly( @@ -1181,10 +1421,21 @@ public void sayHello( v -> assertThat(v).isGreaterThan(0)), experimentalSatisfies( GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - equalTo(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_GRPC_STATUS_CODE, (long) Status.Code.OK.value()), + equalTo(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null), + equalTo(RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null), + 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, "localhost"), equalTo(SERVER_PORT, server.getPort()), equalTo(NETWORK_TYPE, "ipv4"), @@ -1352,11 +1603,16 @@ public void sayHello( @Test void setCapturedRequestMetadata() throws Exception { - String metadataAttributePrefix = "rpc.grpc.request.metadata."; - AttributeKey> clientAttributeKey = - AttributeKey.stringArrayKey(metadataAttributePrefix + CLIENT_REQUEST_METADATA_KEY); - AttributeKey> serverAttributeKey = - AttributeKey.stringArrayKey(metadataAttributePrefix + SERVER_REQUEST_METADATA_KEY); + String oldMetadataAttributePrefix = "rpc.grpc.request.metadata."; + String stableMetadataAttributePrefix = "rpc.request.metadata."; + AttributeKey> oldClientAttributeKey = + AttributeKey.stringArrayKey(oldMetadataAttributePrefix + CLIENT_REQUEST_METADATA_KEY); + AttributeKey> stableClientAttributeKey = + AttributeKey.stringArrayKey(stableMetadataAttributePrefix + CLIENT_REQUEST_METADATA_KEY); + AttributeKey> oldServerAttributeKey = + AttributeKey.stringArrayKey(oldMetadataAttributePrefix + SERVER_REQUEST_METADATA_KEY); + AttributeKey> stableServerAttributeKey = + AttributeKey.stringArrayKey(stableMetadataAttributePrefix + SERVER_REQUEST_METADATA_KEY); String serverMetadataValue = "server-value"; String clientMetadataValue = "client-value"; @@ -1401,16 +1657,32 @@ public void sayHello( 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(clientAttributeKey, singletonList(clientMetadataValue)), - span -> - span.hasName("example.Greeter/SayHello") - .hasKind(SpanKind.SERVER) - .hasParent(trace.getSpan(1)) - .hasAttribute(serverAttributeKey, singletonList(serverMetadataValue)))); + span -> { + span.hasName("example.Greeter/SayHello") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)); + if (emitOldRpcSemconv()) { + span.hasAttribute( + oldClientAttributeKey, singletonList(clientMetadataValue)); + } + if (emitStableRpcSemconv()) { + span.hasAttribute( + stableClientAttributeKey, singletonList(clientMetadataValue)); + } + }, + span -> { + span.hasName("example.Greeter/SayHello") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(1)); + if (emitOldRpcSemconv()) { + span.hasAttribute( + oldServerAttributeKey, singletonList(serverMetadataValue)); + } + if (emitStableRpcSemconv()) { + span.hasAttribute( + stableServerAttributeKey, singletonList(serverMetadataValue)); + } + })); } private ManagedChannel createChannel(Server server) throws Exception { @@ -1449,41 +1721,16 @@ static List addExtraClientAttributes(AttributeAssertion... a private void assertMetrics(Server server, Status.Code statusCode) { boolean hasSizeMetric = statusCode == Status.Code.OK; - testing() - .waitAndAssertMetrics( - "io.opentelemetry.grpc-1.6", - "rpc.server.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfyingExactly( - equalTo(SERVER_ADDRESS, "localhost"), - satisfies( - SERVER_PORT, k -> k.isInstanceOf(Long.class)), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_SYSTEM, "grpc"), - equalTo( - RPC_GRPC_STATUS_CODE, - (long) statusCode.value()), - equalTo(NETWORK_TYPE, "ipv4")))))); - - if (hasSizeMetric) { + if (emitOldRpcSemconv()) { testing() .waitAndAssertMetrics( "io.opentelemetry.grpc-1.6", - "rpc.server.request.size", + "rpc.server.duration", metrics -> metrics.anySatisfy( metric -> assertThat(metric) - .hasUnit("By") + .hasUnit("ms") .hasHistogramSatisfying( histogram -> histogram.hasPointsSatisfying( @@ -1499,82 +1746,87 @@ private void assertMetrics(Server server, Status.Code statusCode) { RPC_GRPC_STATUS_CODE, (long) statusCode.value()), equalTo(NETWORK_TYPE, "ipv4")))))); + + if (hasSizeMetric) { + testing() + .waitAndAssertMetrics( + "io.opentelemetry.grpc-1.6", + "rpc.server.request.size", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("By") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point.hasAttributesSatisfyingExactly( + equalTo(SERVER_ADDRESS, "localhost"), + satisfies( + SERVER_PORT, + k -> k.isInstanceOf(Long.class)), + equalTo(RPC_METHOD, "SayHello"), + equalTo(RPC_SERVICE, "example.Greeter"), + equalTo(RPC_SYSTEM, "grpc"), + equalTo( + RPC_GRPC_STATUS_CODE, + (long) statusCode.value()), + equalTo(NETWORK_TYPE, "ipv4")))))); + testing() + .waitAndAssertMetrics( + "io.opentelemetry.grpc-1.6", + "rpc.server.response.size", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("By") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point.hasAttributesSatisfyingExactly( + equalTo(SERVER_ADDRESS, "localhost"), + satisfies( + SERVER_PORT, + k -> k.isInstanceOf(Long.class)), + equalTo(RPC_METHOD, "SayHello"), + equalTo(RPC_SERVICE, "example.Greeter"), + equalTo(RPC_SYSTEM, "grpc"), + equalTo( + RPC_GRPC_STATUS_CODE, + (long) statusCode.value()), + equalTo(NETWORK_TYPE, "ipv4")))))); + } + testing() .waitAndAssertMetrics( "io.opentelemetry.grpc-1.6", - "rpc.server.response.size", + "rpc.client.duration", metrics -> metrics.anySatisfy( metric -> assertThat(metric) - .hasUnit("By") + .hasUnit("ms") .hasHistogramSatisfying( histogram -> histogram.hasPointsSatisfying( point -> - point.hasAttributesSatisfyingExactly( + point.hasAttributesSatisfying( equalTo(SERVER_ADDRESS, "localhost"), - satisfies( - SERVER_PORT, k -> k.isInstanceOf(Long.class)), + equalTo(SERVER_PORT, server.getPort()), equalTo(RPC_METHOD, "SayHello"), equalTo(RPC_SERVICE, "example.Greeter"), equalTo(RPC_SYSTEM, "grpc"), equalTo( RPC_GRPC_STATUS_CODE, - (long) statusCode.value()), - equalTo(NETWORK_TYPE, "ipv4")))))); - } - - testing() - .waitAndAssertMetrics( - "io.opentelemetry.grpc-1.6", - "rpc.client.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfying( - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, server.getPort()), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_SYSTEM, "grpc"), - equalTo( - RPC_GRPC_STATUS_CODE, - (long) statusCode.value())))))); + (long) statusCode.value())))))); - testing() - .waitAndAssertMetrics( - "io.opentelemetry.grpc-1.6", - "rpc.client.request.size", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasUnit("By") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfying( - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, server.getPort()), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_SYSTEM, "grpc"), - equalTo( - RPC_GRPC_STATUS_CODE, - (long) statusCode.value())))))); - if (hasSizeMetric) { testing() .waitAndAssertMetrics( "io.opentelemetry.grpc-1.6", - "rpc.client.response.size", + "rpc.client.request.size", metrics -> metrics.anySatisfy( metric -> @@ -1593,6 +1845,75 @@ private void assertMetrics(Server server, Status.Code statusCode) { equalTo( RPC_GRPC_STATUS_CODE, (long) statusCode.value())))))); + if (hasSizeMetric) { + testing() + .waitAndAssertMetrics( + "io.opentelemetry.grpc-1.6", + "rpc.client.response.size", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("By") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point.hasAttributesSatisfying( + equalTo(SERVER_ADDRESS, "localhost"), + equalTo(SERVER_PORT, server.getPort()), + equalTo(RPC_METHOD, "SayHello"), + equalTo(RPC_SERVICE, "example.Greeter"), + equalTo(RPC_SYSTEM, "grpc"), + equalTo( + RPC_GRPC_STATUS_CODE, + (long) statusCode.value())))))); + } + } + if (emitStableRpcSemconv()) { + testing() + .waitAndAssertMetrics( + "io.opentelemetry.grpc-1.6", + "rpc.server.call.duration", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("s") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point.hasAttributesSatisfyingExactly( + equalTo(RPC_SYSTEM_NAME, "grpc"), + equalTo(SERVER_ADDRESS, "localhost"), + satisfies( + SERVER_PORT, k -> k.isInstanceOf(Long.class)), + equalTo(RPC_METHOD, "example.Greeter/SayHello"), + equalTo( + RPC_RESPONSE_STATUS_CODE, + statusCode.name())))))); + testing() + .waitAndAssertMetrics( + "io.opentelemetry.grpc-1.6", + "rpc.client.call.duration", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("s") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point.hasAttributesSatisfying( + equalTo(RPC_SYSTEM_NAME, "grpc"), + equalTo(SERVER_ADDRESS, "localhost"), + equalTo(SERVER_PORT, server.getPort()), + equalTo(RPC_METHOD, "example.Greeter/SayHello"), + equalTo( + RPC_RESPONSE_STATUS_CODE, + statusCode.name())))))); } } } diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/rpc/SemconvRpcStabilityUtil.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/rpc/SemconvRpcStabilityUtil.java new file mode 100644 index 000000000000..bd492bd9760b --- /dev/null +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/rpc/SemconvRpcStabilityUtil.java @@ -0,0 +1,40 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.testing.junit.rpc; + +import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableRpcSemconv; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; + +import io.opentelemetry.api.common.AttributeKey; +import java.util.HashMap; +import java.util.Map; + +// until old rpc semconv are dropped in 3.0 +@SuppressWarnings("deprecation") // using deprecated semconv +public final class SemconvRpcStabilityUtil { + + private static final AttributeKey RPC_SYSTEM_NAME = + AttributeKey.stringKey("rpc.system.name"); + + private static final Map, AttributeKey> oldToNewMap = buildMap(); + + private static Map, AttributeKey> buildMap() { + Map, AttributeKey> map = new HashMap<>(); + map.put(RPC_SYSTEM, RPC_SYSTEM_NAME); + return map; + } + + private SemconvRpcStabilityUtil() {} + + @SuppressWarnings("unchecked") + public static AttributeKey maybeStable(AttributeKey oldKey) { + // not testing rpc/dup + if (emitStableRpcSemconv()) { + return (AttributeKey) oldToNewMap.get(oldKey); + } + return oldKey; + } +}