diff --git a/instrumentation-api-incubator/build.gradle.kts b/instrumentation-api-incubator/build.gradle.kts index 3db18fcaf65c..f08df015a3b2 100644 --- a/instrumentation-api-incubator/build.gradle.kts +++ b/instrumentation-api-incubator/build.gradle.kts @@ -86,13 +86,13 @@ tasks { val testStableSemconv by registering(Test::class) { testClassesDirs = sourceSets.test.get().output.classesDirs classpath = sourceSets.test.get().runtimeClasspath - jvmArgs("-Dotel.semconv-stability.opt-in=database,code") + jvmArgs("-Dotel.semconv-stability.opt-in=database,code,rpc") } val testBothSemconv by registering(Test::class) { testClassesDirs = sourceSets.test.get().output.classesDirs classpath = sourceSets.test.get().runtimeClasspath - jvmArgs("-Dotel.semconv-stability.opt-in=database/dup,code/dup") + jvmArgs("-Dotel.semconv-stability.opt-in=database/dup,code/dup,rpc/dup") } check { diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcAttributesGetter.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcAttributesGetter.java index 0da544340f6b..f3ddc224c8c7 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcAttributesGetter.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcAttributesGetter.java @@ -34,4 +34,24 @@ default Long getRequestSize(REQUEST request) { default Long getResponseSize(REQUEST request) { return null; } + + /** + * Returns the fully-qualified RPC method name for stable semconv. + * + *

The default implementation concatenates service + "/" + method. Framework implementations + * can override for efficiency if they already have the fully-qualified name available. + * + * @param request the request object + * @return the fully-qualified RPC method name (e.g., "my.Service/Method"), or null if service or + * method is unavailable + */ + @Nullable + default String getFullMethod(REQUEST request) { + String service = getService(request); + String method = getMethod(request); + if (service == null || method == null) { + return null; + } + return service + "/" + method; + } } diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcClientMetrics.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcClientMetrics.java index 0135bd20f799..bf686c7ec5a4 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcClientMetrics.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcClientMetrics.java @@ -19,8 +19,10 @@ import io.opentelemetry.instrumentation.api.instrumenter.OperationListener; import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics; import io.opentelemetry.instrumentation.api.internal.OperationMetricsUtil; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; +import javax.annotation.Nullable; /** * {@link OperationListener} which keeps track of RPC_CLIENT_REQUEST_METRICS_STATE = ContextKey.named("rpc-client-request-metrics-state"); private static final Logger logger = Logger.getLogger(RpcClientMetrics.class.getName()); - private final DoubleHistogram clientDurationHistogram; - private final LongHistogram clientRequestSize; - private final LongHistogram clientResponseSize; + @Nullable private final DoubleHistogram oldClientDurationHistogram; + @Nullable private final DoubleHistogram stableClientDurationHistogram; + private final LongHistogram oldClientRequestSize; + private final LongHistogram oldClientResponseSize; private RpcClientMetrics(Meter meter) { - DoubleHistogramBuilder durationBuilder = - meter - .histogramBuilder("rpc.client.duration") - .setDescription("The duration of an outbound RPC invocation.") - .setUnit("ms"); - RpcMetricsAdvice.applyClientDurationAdvice(durationBuilder); - clientDurationHistogram = durationBuilder.build(); + // Old metric (milliseconds) + if (SemconvStability.emitOldRpcSemconv()) { + DoubleHistogramBuilder oldDurationBuilder = + meter + .histogramBuilder("rpc.client.duration") + .setDescription("The duration of an outbound RPC invocation.") + .setUnit("ms"); + RpcMetricsAdvice.applyClientDurationAdvice(oldDurationBuilder, false); + oldClientDurationHistogram = oldDurationBuilder.build(); + } else { + oldClientDurationHistogram = null; + } + + // Stable metric (seconds) + if (SemconvStability.emitStableRpcSemconv()) { + DoubleHistogramBuilder stableDurationBuilder = + meter + .histogramBuilder("rpc.client.call.duration") + .setDescription("The duration of an outbound RPC invocation.") + .setUnit("s"); + RpcMetricsAdvice.applyClientDurationAdvice(stableDurationBuilder, true); + stableClientDurationHistogram = stableDurationBuilder.build(); + } else { + stableClientDurationHistogram = null; + } LongHistogramBuilder requestSizeBuilder = meter @@ -55,8 +77,8 @@ private RpcClientMetrics(Meter meter) { .setUnit("By") .setDescription("Measures the size of RPC request messages (uncompressed).") .ofLongs(); - RpcMetricsAdvice.applyClientRequestSizeAdvice(requestSizeBuilder); - clientRequestSize = requestSizeBuilder.build(); + RpcMetricsAdvice.applyDeprecatedClientRequestSizeAdvice(requestSizeBuilder); + oldClientRequestSize = requestSizeBuilder.build(); LongHistogramBuilder responseSizeBuilder = meter @@ -64,8 +86,8 @@ private RpcClientMetrics(Meter meter) { .setUnit("By") .setDescription("Measures the size of RPC response messages (uncompressed).") .ofLongs(); - RpcMetricsAdvice.applyClientRequestSizeAdvice(responseSizeBuilder); - clientResponseSize = responseSizeBuilder.build(); + RpcMetricsAdvice.applyDeprecatedClientRequestSizeAdvice(responseSizeBuilder); + oldClientResponseSize = responseSizeBuilder.build(); } /** @@ -95,17 +117,37 @@ public void onEnd(Context context, Attributes endAttributes, long endNanos) { return; } Attributes attributes = state.startAttributes().toBuilder().putAll(endAttributes).build(); - clientDurationHistogram.record( - (endNanos - state.startTimeNanos()) / NANOS_PER_MS, attributes, context); + double durationNanos = (endNanos - state.startTimeNanos()); + + // Record to old histogram (milliseconds) + if (oldClientDurationHistogram != null) { + oldClientDurationHistogram.record( + durationNanos / NANOS_PER_MS, + SemconvStability.getOldRpcMetricAttributes(attributes), + context); + } - Long rpcClientRequestBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_REQUEST_SIZE); - if (rpcClientRequestBodySize != null) { - clientRequestSize.record(rpcClientRequestBodySize, attributes, context); + // Record to stable histogram (seconds) + if (stableClientDurationHistogram != null) { + stableClientDurationHistogram.record(durationNanos / NANOS_PER_S, attributes, context); } - Long rpcClientResponseBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_RESPONSE_SIZE); - if (rpcClientResponseBodySize != null) { - clientResponseSize.record(rpcClientResponseBodySize, attributes, context); + if (SemconvStability.emitOldRpcSemconv()) { + Long rpcClientRequestBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_REQUEST_SIZE); + if (rpcClientRequestBodySize != null) { + oldClientRequestSize.record( + rpcClientRequestBodySize, + SemconvStability.getOldRpcMetricAttributes(attributes), + context); + } + + Long rpcClientResponseBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_RESPONSE_SIZE); + if (rpcClientResponseBodySize != null) { + oldClientResponseSize.record( + rpcClientResponseBodySize, + SemconvStability.getOldRpcMetricAttributes(attributes), + context); + } } } diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcCommonAttributesExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcCommonAttributesExtractor.java index f731e703586c..d844d887e1a3 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcCommonAttributesExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcCommonAttributesExtractor.java @@ -6,19 +6,27 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.rpc; import static io.opentelemetry.instrumentation.api.internal.AttributesExtractorUtil.internalSet; +import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; import javax.annotation.Nullable; abstract class RpcCommonAttributesExtractor implements AttributesExtractor { - // copied from RpcIncubatingAttributes static final AttributeKey RPC_METHOD = AttributeKey.stringKey("rpc.method"); + + // Stable semconv keys + static final AttributeKey RPC_SYSTEM_NAME = AttributeKey.stringKey("rpc.system.name"); + + // removed in stable semconv (merged into rpc.method) static final AttributeKey RPC_SERVICE = AttributeKey.stringKey("rpc.service"); + + // use RPC_SYSTEM_NAME for stable semconv static final AttributeKey RPC_SYSTEM = AttributeKey.stringKey("rpc.system"); private final RpcAttributesGetter getter; @@ -29,9 +37,22 @@ abstract class RpcCommonAttributesExtractor @Override public final void onStart(AttributesBuilder attributes, Context parentContext, REQUEST request) { - internalSet(attributes, RPC_SYSTEM, getter.getSystem(request)); - internalSet(attributes, RPC_SERVICE, getter.getService(request)); - internalSet(attributes, RPC_METHOD, getter.getMethod(request)); + String system = getter.getSystem(request); + + if (SemconvStability.emitStableRpcSemconv()) { + internalSet( + attributes, + RPC_SYSTEM_NAME, + system == null ? null : SemconvStability.stableRpcSystemName(system)); + internalSet(attributes, RPC_METHOD, getter.getFullMethod(request)); + } + + if (SemconvStability.emitOldRpcSemconv()) { + internalSet(attributes, RPC_SYSTEM, system); + internalSet(attributes, RPC_SERVICE, getter.getService(request)); + internalSet( + attributes, SemconvStability.getOldRpcMethodAttributeKey(), getter.getMethod(request)); + } } @Override @@ -41,6 +62,10 @@ public final void onEnd( REQUEST request, @Nullable RESPONSE response, @Nullable Throwable error) { - // No response attributes + if (SemconvStability.emitStableRpcSemconv()) { + if (error != null) { + internalSet(attributes, ERROR_TYPE, error.getClass().getName()); + } + } } } diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcMetricsAdvice.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcMetricsAdvice.java index af317d876d62..3f9d361fa3e0 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcMetricsAdvice.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcMetricsAdvice.java @@ -5,67 +5,111 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.rpc; -import static java.util.Arrays.asList; - import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.incubator.metrics.ExtendedDoubleHistogramBuilder; import io.opentelemetry.api.incubator.metrics.ExtendedLongHistogramBuilder; import io.opentelemetry.api.metrics.DoubleHistogramBuilder; import io.opentelemetry.api.metrics.LongHistogramBuilder; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; import io.opentelemetry.semconv.NetworkAttributes; import io.opentelemetry.semconv.ServerAttributes; +import java.util.ArrayList; import java.util.List; final class RpcMetricsAdvice { + // Stable semconv key + private static final AttributeKey RPC_RESPONSE_STATUS_CODE = + AttributeKey.stringKey("rpc.response.status_code"); + // copied from RpcIncubatingAttributes + @Deprecated // use RPC_RESPONSE_STATUS_CODE for stable semconv private static final AttributeKey RPC_GRPC_STATUS_CODE = AttributeKey.longKey("rpc.grpc.status_code"); - private static final List> RPC_METRICS_ATTRIBUTE_KEYS = - asList( - RpcCommonAttributesExtractor.RPC_SYSTEM, - RpcCommonAttributesExtractor.RPC_SERVICE, - RpcCommonAttributesExtractor.RPC_METHOD, - RPC_GRPC_STATUS_CODE, - NetworkAttributes.NETWORK_TYPE, - NetworkAttributes.NETWORK_TRANSPORT, - ServerAttributes.SERVER_ADDRESS, - ServerAttributes.SERVER_PORT); - - static void applyClientDurationAdvice(DoubleHistogramBuilder builder) { + + private static final List> RPC_METRICS_DEPRECATED_ATTRIBUTE_KEYS = + buildAttributeKeysList(false); + private static final List> RPC_METRICS_STABLE_ATTRIBUTE_KEYS = + buildAttributeKeysList(true); + + @SuppressWarnings("deprecation") // until old rpc semconv are dropped + private static List> buildAttributeKeysList(boolean stable) { + List> keys = new ArrayList<>(); + + // Add stable or old RPC system key + if (stable) { + keys.add(RpcCommonAttributesExtractor.RPC_SYSTEM_NAME); + } else { + keys.add(RpcCommonAttributesExtractor.RPC_SYSTEM); + } + + // Add RPC service (old only) + if (!stable) { + keys.add(RpcCommonAttributesExtractor.RPC_SERVICE); + } + + keys.add(RpcCommonAttributesExtractor.RPC_METHOD); + + // Add status code key + if (SemconvStability.emitStableRpcSemconv()) { + keys.add(RPC_RESPONSE_STATUS_CODE); + } else { + keys.add(RPC_GRPC_STATUS_CODE); + } + + // Network type only for old semconv + if (!stable) { + keys.add(NetworkAttributes.NETWORK_TYPE); + } + + // Common attributes + keys.add(NetworkAttributes.NETWORK_TRANSPORT); + keys.add(ServerAttributes.SERVER_ADDRESS); + keys.add(ServerAttributes.SERVER_PORT); + + return keys; + } + + private static List> getAttributeKeys(boolean stable) { + return stable ? RPC_METRICS_STABLE_ATTRIBUTE_KEYS : RPC_METRICS_DEPRECATED_ATTRIBUTE_KEYS; + } + + static void applyClientDurationAdvice(DoubleHistogramBuilder builder, boolean stable) { if (!(builder instanceof ExtendedDoubleHistogramBuilder)) { return; } // the list of recommended metrics attributes is from // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/rpc/rpc-metrics.md - ((ExtendedDoubleHistogramBuilder) builder).setAttributesAdvice(RPC_METRICS_ATTRIBUTE_KEYS); + ((ExtendedDoubleHistogramBuilder) builder).setAttributesAdvice(getAttributeKeys(stable)); } - static void applyServerDurationAdvice(DoubleHistogramBuilder builder) { + static void applyServerDurationAdvice(DoubleHistogramBuilder builder, boolean stable) { if (!(builder instanceof ExtendedDoubleHistogramBuilder)) { return; } // the list of recommended metrics attributes is from // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/rpc/rpc-metrics.md - ((ExtendedDoubleHistogramBuilder) builder).setAttributesAdvice(RPC_METRICS_ATTRIBUTE_KEYS); + ((ExtendedDoubleHistogramBuilder) builder).setAttributesAdvice(getAttributeKeys(stable)); } - static void applyClientRequestSizeAdvice(LongHistogramBuilder builder) { + static void applyDeprecatedClientRequestSizeAdvice(LongHistogramBuilder builder) { if (!(builder instanceof ExtendedLongHistogramBuilder)) { return; } // the list of recommended metrics attributes is from // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/rpc/rpc-metrics.md - ((ExtendedLongHistogramBuilder) builder).setAttributesAdvice(RPC_METRICS_ATTRIBUTE_KEYS); + ((ExtendedLongHistogramBuilder) builder) + .setAttributesAdvice(RPC_METRICS_DEPRECATED_ATTRIBUTE_KEYS); } - static void applyServerRequestSizeAdvice(LongHistogramBuilder builder) { + static void applyDeprecatedServerRequestSizeAdvice(LongHistogramBuilder builder) { if (!(builder instanceof ExtendedLongHistogramBuilder)) { return; } // the list of recommended metrics attributes is from // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/rpc/rpc-metrics.md - ((ExtendedLongHistogramBuilder) builder).setAttributesAdvice(RPC_METRICS_ATTRIBUTE_KEYS); + ((ExtendedLongHistogramBuilder) builder) + .setAttributesAdvice(RPC_METRICS_DEPRECATED_ATTRIBUTE_KEYS); } private RpcMetricsAdvice() {} diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcServerMetrics.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcServerMetrics.java index 2ab6755853d2..6c0cb48d6ce2 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcServerMetrics.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcServerMetrics.java @@ -19,8 +19,10 @@ import io.opentelemetry.instrumentation.api.instrumenter.OperationListener; import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics; import io.opentelemetry.instrumentation.api.internal.OperationMetricsUtil; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; +import javax.annotation.Nullable; /** * {@link OperationListener} which keeps track of RPC_SERVER_REQUEST_METRICS_STATE = ContextKey.named("rpc-server-request-metrics-state"); private static final Logger logger = Logger.getLogger(RpcServerMetrics.class.getName()); - private final DoubleHistogram serverDurationHistogram; - private final LongHistogram serverRequestSize; - private final LongHistogram serverResponseSize; + @Nullable private final DoubleHistogram oldServerDurationHistogram; + @Nullable private final DoubleHistogram stableServerDurationHistogram; + private final LongHistogram oldServerRequestSize; + private final LongHistogram oldServerResponseSize; private RpcServerMetrics(Meter meter) { - DoubleHistogramBuilder durationBuilder = - meter - .histogramBuilder("rpc.server.duration") - .setDescription("The duration of an inbound RPC invocation.") - .setUnit("ms"); - RpcMetricsAdvice.applyServerDurationAdvice(durationBuilder); - serverDurationHistogram = durationBuilder.build(); + // Old metric (milliseconds) + if (SemconvStability.emitOldRpcSemconv()) { + DoubleHistogramBuilder oldDurationBuilder = + meter + .histogramBuilder("rpc.server.duration") + .setDescription("The duration of an inbound RPC invocation.") + .setUnit("ms"); + RpcMetricsAdvice.applyServerDurationAdvice(oldDurationBuilder, false); + oldServerDurationHistogram = oldDurationBuilder.build(); + } else { + oldServerDurationHistogram = null; + } + + // Stable metric (seconds) + if (SemconvStability.emitStableRpcSemconv()) { + DoubleHistogramBuilder stableDurationBuilder = + meter + .histogramBuilder("rpc.server.call.duration") + .setDescription("The duration of an inbound RPC invocation.") + .setUnit("s"); + RpcMetricsAdvice.applyServerDurationAdvice(stableDurationBuilder, true); + stableServerDurationHistogram = stableDurationBuilder.build(); + } else { + stableServerDurationHistogram = null; + } LongHistogramBuilder requestSizeBuilder = meter @@ -55,8 +77,8 @@ private RpcServerMetrics(Meter meter) { .setUnit("By") .setDescription("Measures the size of RPC request messages (uncompressed).") .ofLongs(); - RpcMetricsAdvice.applyServerRequestSizeAdvice(requestSizeBuilder); - serverRequestSize = requestSizeBuilder.build(); + RpcMetricsAdvice.applyDeprecatedServerRequestSizeAdvice(requestSizeBuilder); + oldServerRequestSize = requestSizeBuilder.build(); LongHistogramBuilder responseSizeBuilder = meter @@ -64,8 +86,8 @@ private RpcServerMetrics(Meter meter) { .setUnit("By") .setDescription("Measures the size of RPC response messages (uncompressed).") .ofLongs(); - RpcMetricsAdvice.applyServerRequestSizeAdvice(responseSizeBuilder); - serverResponseSize = responseSizeBuilder.build(); + RpcMetricsAdvice.applyDeprecatedServerRequestSizeAdvice(responseSizeBuilder); + oldServerResponseSize = responseSizeBuilder.build(); } /** @@ -95,17 +117,37 @@ public void onEnd(Context context, Attributes endAttributes, long endNanos) { return; } Attributes attributes = state.startAttributes().toBuilder().putAll(endAttributes).build(); - serverDurationHistogram.record( - (endNanos - state.startTimeNanos()) / NANOS_PER_MS, attributes, context); + double durationNanos = (endNanos - state.startTimeNanos()); + + // Record to old histogram (milliseconds) + if (oldServerDurationHistogram != null) { + oldServerDurationHistogram.record( + durationNanos / NANOS_PER_MS, + SemconvStability.getOldRpcMetricAttributes(attributes), + context); + } - Long rpcServerRequestBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_REQUEST_SIZE); - if (rpcServerRequestBodySize != null) { - serverRequestSize.record(rpcServerRequestBodySize, attributes, context); + // Record to stable histogram (seconds) + if (stableServerDurationHistogram != null) { + stableServerDurationHistogram.record(durationNanos / NANOS_PER_S, attributes, context); } - Long rpcServerResponseBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_RESPONSE_SIZE); - if (rpcServerResponseBodySize != null) { - serverResponseSize.record(rpcServerResponseBodySize, attributes, context); + if (SemconvStability.emitOldRpcSemconv()) { + Long rpcServerRequestBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_REQUEST_SIZE); + if (rpcServerRequestBodySize != null) { + oldServerRequestSize.record( + rpcServerRequestBodySize, + SemconvStability.getOldRpcMetricAttributes(attributes), + context); + } + + Long rpcServerResponseBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_RESPONSE_SIZE); + if (rpcServerResponseBodySize != null) { + oldServerResponseSize.record( + rpcServerResponseBodySize, + SemconvStability.getOldRpcMetricAttributes(attributes), + context); + } } } diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcAttributesExtractorTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcAttributesExtractorTest.java index dfca442b2573..bc5d05b159b8 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcAttributesExtractorTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcAttributesExtractorTest.java @@ -8,12 +8,15 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static org.assertj.core.api.Assertions.entry; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; -import io.opentelemetry.semconv.incubating.RpcIncubatingAttributes; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; @@ -48,6 +51,20 @@ void client() { testExtractor(RpcClientAttributesExtractor.create(TestGetter.INSTANCE)); } + // Stable semconv keys + private static final AttributeKey RPC_SYSTEM_NAME = + AttributeKey.stringKey("rpc.system.name"); + + // Old semconv keys (from RpcIncubatingAttributes) + private static final AttributeKey RPC_SYSTEM = + io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; + + private static final AttributeKey RPC_SERVICE = + io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; + + private static final AttributeKey RPC_METHOD = + io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; + private static void testExtractor(AttributesExtractor, Void> extractor) { Map request = new HashMap<>(); request.put("service", "my.Service"); @@ -57,16 +74,28 @@ private static void testExtractor(AttributesExtractor, Void> AttributesBuilder attributes = Attributes.builder(); extractor.onStart(attributes, context, request); - assertThat(attributes.build()) - .containsOnly( - entry(RpcIncubatingAttributes.RPC_SYSTEM, "test"), - entry(RpcIncubatingAttributes.RPC_SERVICE, "my.Service"), - entry(RpcIncubatingAttributes.RPC_METHOD, "Method")); + + // Build expected entries list based on semconv mode + List, ?>> expectedEntries = new ArrayList<>(); + + if (SemconvStability.emitStableRpcSemconv()) { + expectedEntries.add(entry(RPC_SYSTEM_NAME, "test")); + expectedEntries.add(entry(RPC_METHOD, "my.Service/Method")); + } + + if (SemconvStability.emitOldRpcSemconv()) { + expectedEntries.add(entry(RPC_SYSTEM, "test")); + expectedEntries.add(entry(RPC_SERVICE, "my.Service")); + expectedEntries.add(entry(SemconvStability.getOldRpcMethodAttributeKey(), "Method")); + } + + // safe conversion for test assertions + @SuppressWarnings({"unchecked", "rawtypes"}) + Map.Entry, ?>[] expectedArray = + (Map.Entry, ?>[]) expectedEntries.toArray(new Map.Entry[0]); + assertThat(attributes.build()).containsOnly(expectedArray); + extractor.onEnd(attributes, context, request, null, null); - assertThat(attributes.build()) - .containsOnly( - entry(RpcIncubatingAttributes.RPC_SYSTEM, "test"), - entry(RpcIncubatingAttributes.RPC_SERVICE, "my.Service"), - entry(RpcIncubatingAttributes.RPC_METHOD, "Method")); + assertThat(attributes.build()).containsOnly(expectedArray); } } diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcClientMetricsTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcClientMetricsTest.java index ec37a9bb4439..b4a1000c5ff0 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcClientMetricsTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcClientMetricsTest.java @@ -5,21 +5,32 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.rpc; +import static io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcCommonAttributesExtractor.RPC_SYSTEM_NAME; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.api.trace.TraceFlags; import io.opentelemetry.api.trace.TraceState; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.OperationListener; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import io.opentelemetry.semconv.NetworkAttributes; import io.opentelemetry.semconv.ServerAttributes; -import io.opentelemetry.semconv.incubating.RpcIncubatingAttributes; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; @@ -34,19 +45,10 @@ void collectsMetrics() { OperationListener listener = RpcClientMetrics.get().create(meterProvider.get("test")); Attributes requestAttributes1 = - Attributes.builder() - .put(RpcIncubatingAttributes.RPC_SYSTEM, "grpc") - .put(RpcIncubatingAttributes.RPC_SERVICE, "myservice.EchoService") - .put(RpcIncubatingAttributes.RPC_METHOD, "exampleMethod") - .put(RpcSizeAttributesExtractor.RPC_REQUEST_SIZE, 10) - .build(); + buildRequestAttributes("grpc", "myservice.EchoService", "exampleMethod", true); Attributes requestAttributes2 = - Attributes.builder() - .put(RpcIncubatingAttributes.RPC_SYSTEM, "grpc") - .put(RpcIncubatingAttributes.RPC_SERVICE, "myservice.EchoService") - .put(RpcIncubatingAttributes.RPC_METHOD, "exampleMethod") - .build(); + buildRequestAttributes("grpc", "myservice.EchoService", "exampleMethod", false); Attributes responseAttributes1 = Attributes.builder() @@ -83,119 +85,236 @@ void collectsMetrics() { listener.onEnd(context1, responseAttributes1, nanos(250)); - assertThat(metricReader.collectAllMetrics()) - .satisfiesExactlyInAnyOrder( - metric -> - assertThat(metric) - .hasName("rpc.client.response.size") - .hasUnit("By") - .hasDescription("Measures the size of RPC response messages (uncompressed).") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point - .hasSum(20 /* bytes */) - .hasAttributesSatisfyingExactly( - equalTo(RpcIncubatingAttributes.RPC_SYSTEM, "grpc"), - equalTo( - RpcIncubatingAttributes.RPC_SERVICE, - "myservice.EchoService"), - equalTo( - RpcIncubatingAttributes.RPC_METHOD, - "exampleMethod"), - equalTo(ServerAttributes.SERVER_ADDRESS, "example.com"), - equalTo(ServerAttributes.SERVER_PORT, 8080), - equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp"), - equalTo(NetworkAttributes.NETWORK_TYPE, "ipv4")) - .hasExemplarsSatisfying( - exemplar -> - exemplar - .hasTraceId("ff01020304050600ff0a0b0c0d0e0f00") - .hasSpanId("090a0b0c0d0e0f00")))), - metric -> - assertThat(metric) - .hasName("rpc.client.request.size") - .hasUnit("By") - .hasDescription("Measures the size of RPC request messages (uncompressed).") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point - .hasSum(10 /* bytes */) - .hasAttributesSatisfyingExactly( - equalTo(RpcIncubatingAttributes.RPC_SYSTEM, "grpc"), - equalTo( - RpcIncubatingAttributes.RPC_SERVICE, - "myservice.EchoService"), - equalTo( - RpcIncubatingAttributes.RPC_METHOD, - "exampleMethod"), - equalTo(ServerAttributes.SERVER_ADDRESS, "example.com"), - equalTo(ServerAttributes.SERVER_PORT, 8080), - equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp"), - equalTo(NetworkAttributes.NETWORK_TYPE, "ipv4")) - .hasExemplarsSatisfying( - exemplar -> - exemplar - .hasTraceId("ff01020304050600ff0a0b0c0d0e0f00") - .hasSpanId("090a0b0c0d0e0f00")))), - metric -> - assertThat(metric) - .hasName("rpc.client.duration") - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point - .hasSum(150 /* millis */) - .hasAttributesSatisfyingExactly( - equalTo(RpcIncubatingAttributes.RPC_SYSTEM, "grpc"), - equalTo( - RpcIncubatingAttributes.RPC_SERVICE, - "myservice.EchoService"), - equalTo( - RpcIncubatingAttributes.RPC_METHOD, - "exampleMethod"), - equalTo(ServerAttributes.SERVER_ADDRESS, "example.com"), - equalTo(ServerAttributes.SERVER_PORT, 8080), - equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp"), - equalTo(NetworkAttributes.NETWORK_TYPE, "ipv4")) - .hasExemplarsSatisfying( - exemplar -> - exemplar - .hasTraceId("ff01020304050600ff0a0b0c0d0e0f00") - .hasSpanId("090a0b0c0d0e0f00"))))); + // Calculate expected metric count: + // - dup mode: old duration + stable duration + request.size + response.size = 4 + // - old-only mode: old duration + request.size + response.size = 3 + // - stable-only mode: stable duration only = 1 + int expectedMetricCount = 1; // At minimum, we always have one duration metric + if (SemconvStability.emitOldRpcSemconv() && SemconvStability.emitStableRpcSemconv()) { + expectedMetricCount = 4; // Both durations + both size metrics + } else if (SemconvStability.emitOldRpcSemconv()) { + expectedMetricCount = 3; // Old duration + both size metrics + } + + // Collect metrics once (delta reader consumes on each call) + Collection metrics1 = metricReader.collectAllMetrics(); + assertThat(metrics1).hasSize(expectedMetricCount); + + // Build expected attributes for OLD metrics (size + old duration) - alphabetically sorted + List oldMetricAttributes1 = new ArrayList<>(); + if (SemconvStability.emitOldRpcSemconv()) { + oldMetricAttributes1.add(equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp")); + oldMetricAttributes1.add(equalTo(NetworkAttributes.NETWORK_TYPE, "ipv4")); + oldMetricAttributes1.add(equalTo(RPC_METHOD, "exampleMethod")); + oldMetricAttributes1.add(equalTo(RPC_SERVICE, "myservice.EchoService")); + oldMetricAttributes1.add(equalTo(RPC_SYSTEM, "grpc")); + oldMetricAttributes1.add(equalTo(ServerAttributes.SERVER_ADDRESS, "example.com")); + oldMetricAttributes1.add(equalTo(ServerAttributes.SERVER_PORT, 8080)); + } + + // Size metrics are only recorded in old semconv mode + if (SemconvStability.emitOldRpcSemconv()) { + assertThat(metrics1) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.client.response.size") + .hasUnit("By") + .hasDescription("Measures the size of RPC response messages (uncompressed).") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(20 /* bytes */) + .hasAttributesSatisfyingExactly( + oldMetricAttributes1.toArray( + new AttributeAssertion[0])) + .hasExemplarsSatisfying( + exemplar -> + exemplar + .hasTraceId( + "ff01020304050600ff0a0b0c0d0e0f00") + .hasSpanId("090a0b0c0d0e0f00"))))) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.client.request.size") + .hasUnit("By") + .hasDescription("Measures the size of RPC request messages (uncompressed).") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(10 /* bytes */) + .hasAttributesSatisfyingExactly( + oldMetricAttributes1.toArray( + new AttributeAssertion[0])) + .hasExemplarsSatisfying( + exemplar -> + exemplar + .hasTraceId( + "ff01020304050600ff0a0b0c0d0e0f00") + .hasSpanId("090a0b0c0d0e0f00"))))); + } + + // Assert stable duration metric if emitting stable semconv + if (SemconvStability.emitStableRpcSemconv()) { + // Build expected attributes for STABLE metrics - alphabetically sorted + List stableMetricAttributes1 = new ArrayList<>(); + stableMetricAttributes1.add(equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp")); + stableMetricAttributes1.add(equalTo(RPC_METHOD, "myservice.EchoService/exampleMethod")); + stableMetricAttributes1.add( + equalTo( + AttributeKey.stringKey("rpc.system.name"), + SemconvStability.stableRpcSystemName("grpc"))); + stableMetricAttributes1.add(equalTo(ServerAttributes.SERVER_ADDRESS, "example.com")); + stableMetricAttributes1.add(equalTo(ServerAttributes.SERVER_PORT, 8080)); + + assertThat(metrics1) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.client.call.duration") + .hasUnit("s") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(0.15) + .hasAttributesSatisfyingExactly( + stableMetricAttributes1.toArray( + new AttributeAssertion[0])) + .hasExemplarsSatisfying( + exemplar -> + exemplar + .hasTraceId( + "ff01020304050600ff0a0b0c0d0e0f00") + .hasSpanId("090a0b0c0d0e0f00"))))); + } + + // Assert old duration metric if emitting old semconv + if (SemconvStability.emitOldRpcSemconv()) { + assertThat(metrics1) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.client.duration") + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(150.0) + .hasAttributesSatisfyingExactly( + oldMetricAttributes1.toArray( + new AttributeAssertion[0])) + .hasExemplarsSatisfying( + exemplar -> + exemplar + .hasTraceId( + "ff01020304050600ff0a0b0c0d0e0f00") + .hasSpanId("090a0b0c0d0e0f00"))))); + } listener.onEnd(context2, responseAttributes2, nanos(300)); - assertThat(metricReader.collectAllMetrics()) - .satisfiesExactlyInAnyOrder( - metric -> - assertThat(metric) - .hasName("rpc.client.duration") - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point - .hasSum(150 /* millis */) - .hasAttributesSatisfyingExactly( - equalTo(RpcIncubatingAttributes.RPC_SYSTEM, "grpc"), - equalTo( - RpcIncubatingAttributes.RPC_SERVICE, - "myservice.EchoService"), - equalTo( - RpcIncubatingAttributes.RPC_METHOD, - "exampleMethod"), - equalTo(ServerAttributes.SERVER_PORT, 8080), - equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp"))))); + // Collect metrics once (delta reader consumes on each call) + // Note: metrics2 doesn't include size metrics since context2 has no size attributes + int expectedMetricCount2 = + SemconvStability.emitOldRpcSemconv() && SemconvStability.emitStableRpcSemconv() ? 2 : 1; + Collection metrics2 = metricReader.collectAllMetrics(); + assertThat(metrics2).hasSize(expectedMetricCount2); + + // Build expected attributes for OLD metrics (no server.address or network.type for context2) - + // alphabetically sorted + List oldMetricAttributes2 = new ArrayList<>(); + if (SemconvStability.emitOldRpcSemconv()) { + oldMetricAttributes2.add(equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp")); + oldMetricAttributes2.add(equalTo(RPC_METHOD, "exampleMethod")); + oldMetricAttributes2.add(equalTo(RPC_SERVICE, "myservice.EchoService")); + oldMetricAttributes2.add(equalTo(RPC_SYSTEM, "grpc")); + oldMetricAttributes2.add(equalTo(ServerAttributes.SERVER_PORT, 8080)); + } + + // Build expected attributes for STABLE metrics (no server.address for context2) - + // alphabetically sorted + List stableMetricAttributes2 = new ArrayList<>(); + if (SemconvStability.emitStableRpcSemconv()) { + stableMetricAttributes2.add(equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp")); + stableMetricAttributes2.add(equalTo(RPC_METHOD, "myservice.EchoService/exampleMethod")); + stableMetricAttributes2.add( + equalTo( + AttributeKey.stringKey("rpc.system.name"), + SemconvStability.stableRpcSystemName("grpc"))); + stableMetricAttributes2.add(equalTo(ServerAttributes.SERVER_PORT, 8080)); + } + + // Assert stable duration metric if emitting stable semconv + if (SemconvStability.emitStableRpcSemconv()) { + assertThat(metrics2) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.client.call.duration") + .hasUnit("s") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(0.15) + .hasAttributesSatisfyingExactly( + stableMetricAttributes2.toArray( + new AttributeAssertion[0]))))); + } + + // Assert old duration metric if emitting old semconv + if (SemconvStability.emitOldRpcSemconv()) { + assertThat(metrics2) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.client.duration") + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(150.0) + .hasAttributesSatisfyingExactly( + oldMetricAttributes2.toArray( + new AttributeAssertion[0]))))); + } } private static long nanos(int millis) { return TimeUnit.MILLISECONDS.toNanos(millis); } + + private static Attributes buildRequestAttributes( + String system, String service, String method, boolean withSize) { + AttributesBuilder builder = Attributes.builder(); + + if (SemconvStability.emitOldRpcSemconv()) { + builder.put(RPC_SYSTEM, system); + builder.put(RPC_SERVICE, service); + builder.put(SemconvStability.getOldRpcMethodAttributeKey(), method); + } + + if (SemconvStability.emitStableRpcSemconv()) { + builder.put(RPC_SYSTEM_NAME, SemconvStability.stableRpcSystemName(system)); + builder.put(RPC_METHOD, service + "/" + method); + } + + if (withSize) { + builder.put(RpcSizeAttributesExtractor.RPC_REQUEST_SIZE, 10); + } + + return builder.build(); + } } diff --git a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcServerMetricsTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcServerMetricsTest.java index e0e622adb2ac..863fc662f735 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcServerMetricsTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcServerMetricsTest.java @@ -5,21 +5,33 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.rpc; +import static io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcCommonAttributesExtractor.RPC_SYSTEM_NAME; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.api.trace.TraceFlags; import io.opentelemetry.api.trace.TraceState; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.OperationListener; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import io.opentelemetry.semconv.NetworkAttributes; import io.opentelemetry.semconv.ServerAttributes; import io.opentelemetry.semconv.incubating.RpcIncubatingAttributes; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; @@ -34,19 +46,10 @@ void collectsMetrics() { OperationListener listener = RpcServerMetrics.get().create(meterProvider.get("test")); Attributes requestAttributes1 = - Attributes.builder() - .put(RpcIncubatingAttributes.RPC_SYSTEM, "grpc") - .put(RpcIncubatingAttributes.RPC_SERVICE, "myservice.EchoService") - .put(RpcIncubatingAttributes.RPC_METHOD, "exampleMethod") - .put(RpcSizeAttributesExtractor.RPC_REQUEST_SIZE, 10) - .build(); + buildRequestAttributes("grpc", "myservice.EchoService", "exampleMethod", true); Attributes requestAttributes2 = - Attributes.builder() - .put(RpcIncubatingAttributes.RPC_SYSTEM, "grpc") - .put(RpcIncubatingAttributes.RPC_SERVICE, "myservice.EchoService") - .put(RpcIncubatingAttributes.RPC_METHOD, "exampleMethod") - .build(); + buildRequestAttributes("grpc", "myservice.EchoService", "exampleMethod", false); Attributes responseAttributes1 = Attributes.builder() @@ -85,119 +88,238 @@ void collectsMetrics() { listener.onEnd(context1, responseAttributes1, nanos(250)); - assertThat(metricReader.collectAllMetrics()) - .satisfiesExactlyInAnyOrder( - metric -> - assertThat(metric) - .hasName("rpc.server.duration") - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point - .hasSum(150 /* millis */) - .hasAttributesSatisfyingExactly( - equalTo(RpcIncubatingAttributes.RPC_SYSTEM, "grpc"), - equalTo( - RpcIncubatingAttributes.RPC_SERVICE, - "myservice.EchoService"), - equalTo( - RpcIncubatingAttributes.RPC_METHOD, - "exampleMethod"), - equalTo(ServerAttributes.SERVER_ADDRESS, "example.com"), - equalTo(ServerAttributes.SERVER_PORT, 8080), - equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp"), - equalTo(NetworkAttributes.NETWORK_TYPE, "ipv4")) - .hasExemplarsSatisfying( - exemplar -> - exemplar - .hasTraceId("ff01020304050600ff0a0b0c0d0e0f00") - .hasSpanId("090a0b0c0d0e0f00")))), - metric -> - assertThat(metric) - .hasName("rpc.server.response.size") - .hasUnit("By") - .hasDescription("Measures the size of RPC response messages (uncompressed).") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point - .hasSum(20 /* bytes */) - .hasAttributesSatisfyingExactly( - equalTo(RpcIncubatingAttributes.RPC_SYSTEM, "grpc"), - equalTo( - RpcIncubatingAttributes.RPC_SERVICE, - "myservice.EchoService"), - equalTo( - RpcIncubatingAttributes.RPC_METHOD, - "exampleMethod"), - equalTo(ServerAttributes.SERVER_ADDRESS, "example.com"), - equalTo(ServerAttributes.SERVER_PORT, 8080), - equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp"), - equalTo(NetworkAttributes.NETWORK_TYPE, "ipv4")) - .hasExemplarsSatisfying( - exemplar -> - exemplar - .hasTraceId("ff01020304050600ff0a0b0c0d0e0f00") - .hasSpanId("090a0b0c0d0e0f00")))), - metric -> - assertThat(metric) - .hasName("rpc.server.request.size") - .hasUnit("By") - .hasDescription("Measures the size of RPC request messages (uncompressed).") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point - .hasSum(10 /* bytes */) - .hasAttributesSatisfyingExactly( - equalTo(RpcIncubatingAttributes.RPC_SYSTEM, "grpc"), - equalTo( - RpcIncubatingAttributes.RPC_SERVICE, - "myservice.EchoService"), - equalTo( - RpcIncubatingAttributes.RPC_METHOD, - "exampleMethod"), - equalTo(ServerAttributes.SERVER_ADDRESS, "example.com"), - equalTo(ServerAttributes.SERVER_PORT, 8080), - equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp"), - equalTo(NetworkAttributes.NETWORK_TYPE, "ipv4")) - .hasExemplarsSatisfying( - exemplar -> - exemplar - .hasTraceId("ff01020304050600ff0a0b0c0d0e0f00") - .hasSpanId("090a0b0c0d0e0f00"))))); + // Calculate expected metric count: + // - dup mode: old duration + stable duration + request.size + response.size = 4 + // - old-only mode: old duration + request.size + response.size = 3 + // - stable-only mode: stable duration only = 1 + int expectedMetricCount = 1; // At minimum, we always have one duration metric + if (SemconvStability.emitOldRpcSemconv() && SemconvStability.emitStableRpcSemconv()) { + expectedMetricCount = 4; // Both durations + both size metrics + } else if (SemconvStability.emitOldRpcSemconv()) { + expectedMetricCount = 3; // Old duration + both size metrics + } + + // Collect metrics once (delta reader consumes on each call) + Collection metrics1 = metricReader.collectAllMetrics(); + assertThat(metrics1).hasSize(expectedMetricCount); + + // Build expected attributes for OLD metrics (size + old duration) - alphabetically sorted + List oldMetricAttributes1 = new ArrayList<>(); + if (SemconvStability.emitOldRpcSemconv()) { + oldMetricAttributes1.add(equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp")); + oldMetricAttributes1.add(equalTo(NetworkAttributes.NETWORK_TYPE, "ipv4")); + oldMetricAttributes1.add(equalTo(RpcIncubatingAttributes.RPC_METHOD, "exampleMethod")); + oldMetricAttributes1.add(equalTo(RPC_SERVICE, "myservice.EchoService")); + oldMetricAttributes1.add(equalTo(RPC_SYSTEM, "grpc")); + oldMetricAttributes1.add(equalTo(ServerAttributes.SERVER_ADDRESS, "example.com")); + oldMetricAttributes1.add(equalTo(ServerAttributes.SERVER_PORT, 8080)); + } + + // Size metrics are only recorded in old semconv mode + if (SemconvStability.emitOldRpcSemconv()) { + assertThat(metrics1) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.server.response.size") + .hasUnit("By") + .hasDescription("Measures the size of RPC response messages (uncompressed).") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(20 /* bytes */) + .hasAttributesSatisfyingExactly( + oldMetricAttributes1.toArray( + new AttributeAssertion[0])) + .hasExemplarsSatisfying( + exemplar -> + exemplar + .hasTraceId( + "ff01020304050600ff0a0b0c0d0e0f00") + .hasSpanId("090a0b0c0d0e0f00"))))) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.server.request.size") + .hasUnit("By") + .hasDescription("Measures the size of RPC request messages (uncompressed).") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(10 /* bytes */) + .hasAttributesSatisfyingExactly( + oldMetricAttributes1.toArray( + new AttributeAssertion[0])) + .hasExemplarsSatisfying( + exemplar -> + exemplar + .hasTraceId( + "ff01020304050600ff0a0b0c0d0e0f00") + .hasSpanId("090a0b0c0d0e0f00"))))); + } + + // Assert stable duration metric if emitting stable semconv + if (SemconvStability.emitStableRpcSemconv()) { + // Build expected attributes for STABLE metrics - alphabetically sorted + List stableMetricAttributes1 = new ArrayList<>(); + stableMetricAttributes1.add(equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp")); + stableMetricAttributes1.add( + equalTo(RpcIncubatingAttributes.RPC_METHOD, "myservice.EchoService/exampleMethod")); + stableMetricAttributes1.add( + equalTo( + AttributeKey.stringKey("rpc.system.name"), + SemconvStability.stableRpcSystemName("grpc"))); + stableMetricAttributes1.add(equalTo(ServerAttributes.SERVER_ADDRESS, "example.com")); + stableMetricAttributes1.add(equalTo(ServerAttributes.SERVER_PORT, 8080)); + + assertThat(metrics1) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.server.call.duration") + .hasUnit("s") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(0.15) + .hasAttributesSatisfyingExactly( + stableMetricAttributes1.toArray( + new AttributeAssertion[0])) + .hasExemplarsSatisfying( + exemplar -> + exemplar + .hasTraceId( + "ff01020304050600ff0a0b0c0d0e0f00") + .hasSpanId("090a0b0c0d0e0f00"))))); + } + + // Assert old duration metric if emitting old semconv + if (SemconvStability.emitOldRpcSemconv()) { + assertThat(metrics1) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.server.duration") + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(150.0) + .hasAttributesSatisfyingExactly( + oldMetricAttributes1.toArray( + new AttributeAssertion[0])) + .hasExemplarsSatisfying( + exemplar -> + exemplar + .hasTraceId( + "ff01020304050600ff0a0b0c0d0e0f00") + .hasSpanId("090a0b0c0d0e0f00"))))); + } listener.onEnd(context2, responseAttributes2, nanos(300)); - assertThat(metricReader.collectAllMetrics()) - .satisfiesExactlyInAnyOrder( - metric -> - assertThat(metric) - .hasName("rpc.server.duration") - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point - .hasSum(150 /* millis */) - .hasAttributesSatisfyingExactly( - equalTo(RpcIncubatingAttributes.RPC_SYSTEM, "grpc"), - equalTo( - RpcIncubatingAttributes.RPC_SERVICE, - "myservice.EchoService"), - equalTo( - RpcIncubatingAttributes.RPC_METHOD, - "exampleMethod"), - equalTo(ServerAttributes.SERVER_PORT, 8080), - equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp"))))); + // Collect metrics once (delta reader consumes on each call) + // Note: metrics2 doesn't include size metrics since context2 has no size attributes + int expectedMetricCount2 = + SemconvStability.emitOldRpcSemconv() && SemconvStability.emitStableRpcSemconv() ? 2 : 1; + Collection metrics2 = metricReader.collectAllMetrics(); + assertThat(metrics2).hasSize(expectedMetricCount2); + + // Build expected attributes for OLD metrics (no server.address or network.type for context2) - + // alphabetically sorted + List oldMetricAttributes2 = new ArrayList<>(); + if (SemconvStability.emitOldRpcSemconv()) { + oldMetricAttributes2.add(equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp")); + oldMetricAttributes2.add(equalTo(RpcIncubatingAttributes.RPC_METHOD, "exampleMethod")); + oldMetricAttributes2.add(equalTo(RPC_SERVICE, "myservice.EchoService")); + oldMetricAttributes2.add(equalTo(RPC_SYSTEM, "grpc")); + oldMetricAttributes2.add(equalTo(ServerAttributes.SERVER_PORT, 8080)); + } + + // Build expected attributes for STABLE metrics (no server.address for context2) - + // alphabetically sorted + List stableMetricAttributes2 = new ArrayList<>(); + if (SemconvStability.emitStableRpcSemconv()) { + stableMetricAttributes2.add(equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp")); + stableMetricAttributes2.add( + equalTo(RpcIncubatingAttributes.RPC_METHOD, "myservice.EchoService/exampleMethod")); + stableMetricAttributes2.add( + equalTo( + AttributeKey.stringKey("rpc.system.name"), + SemconvStability.stableRpcSystemName("grpc"))); + stableMetricAttributes2.add(equalTo(ServerAttributes.SERVER_PORT, 8080)); + } + + // Assert stable duration metric if emitting stable semconv + if (SemconvStability.emitStableRpcSemconv()) { + assertThat(metrics2) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.server.call.duration") + .hasUnit("s") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(0.15) + .hasAttributesSatisfyingExactly( + stableMetricAttributes2.toArray( + new AttributeAssertion[0]))))); + } + + // Assert old duration metric if emitting old semconv + if (SemconvStability.emitOldRpcSemconv()) { + assertThat(metrics2) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.server.duration") + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(150.0) + .hasAttributesSatisfyingExactly( + oldMetricAttributes2.toArray( + new AttributeAssertion[0]))))); + } } private static long nanos(int millis) { return TimeUnit.MILLISECONDS.toNanos(millis); } + + private static Attributes buildRequestAttributes( + String system, String service, String method, boolean withSize) { + AttributesBuilder builder = Attributes.builder(); + + if (SemconvStability.emitOldRpcSemconv()) { + builder.put(RPC_SYSTEM, system); + builder.put(RPC_SERVICE, service); + builder.put(SemconvStability.getOldRpcMethodAttributeKey(), method); + } + + if (SemconvStability.emitStableRpcSemconv()) { + builder.put(RPC_SYSTEM_NAME, SemconvStability.stableRpcSystemName(system)); + builder.put(RPC_METHOD, service + "/" + method); + } + + if (withSize) { + builder.put(RpcSizeAttributesExtractor.RPC_REQUEST_SIZE, 10); + } + + return builder.build(); + } } diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/SemconvStability.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/SemconvStability.java index e54a62530b67..0c1db7aa7f85 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/SemconvStability.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/internal/SemconvStability.java @@ -7,6 +7,8 @@ import static java.util.Arrays.asList; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -18,12 +20,22 @@ */ public final class SemconvStability { + // copied from RpcIncubatingAttributes + private static final AttributeKey RPC_METHOD = AttributeKey.stringKey("rpc.method"); + + // virtual key to avoid clash with stable rpc.method + private static final AttributeKey RPC_METHOD_OLD = + AttributeKey.stringKey("rpc.method.deprecated"); + private static final boolean emitOldDatabaseSemconv; private static final boolean emitStableDatabaseSemconv; private static final boolean emitOldCodeSemconv; private static final boolean emitStableCodeSemconv; + private static final boolean emitOldRpcSemconv; + private static final boolean emitStableRpcSemconv; + static { boolean oldDatabase = true; boolean stableDatabase = false; @@ -31,6 +43,9 @@ public final class SemconvStability { boolean oldCode = true; boolean stableCode = false; + boolean oldRpc = true; + boolean stableRpc = false; + String value = ConfigPropertiesUtil.getString("otel.semconv-stability.opt-in"); if (value != null) { Set values = new HashSet<>(asList(value.split(","))); @@ -55,6 +70,15 @@ public final class SemconvStability { oldCode = true; stableCode = true; } + + if (values.contains("rpc")) { + oldRpc = false; + stableRpc = true; + } + if (values.contains("rpc/dup")) { + oldRpc = true; + stableRpc = true; + } } emitOldDatabaseSemconv = oldDatabase; @@ -62,6 +86,9 @@ public final class SemconvStability { emitOldCodeSemconv = oldCode; emitStableCodeSemconv = stableCode; + + emitOldRpcSemconv = oldRpc; + emitStableRpcSemconv = stableRpc; } public static boolean emitOldDatabaseSemconv() { @@ -105,5 +132,41 @@ public static boolean isEmitStableCodeSemconv() { return emitStableCodeSemconv; } + public static boolean emitOldRpcSemconv() { + return emitOldRpcSemconv; + } + + public static boolean emitStableRpcSemconv() { + return emitStableRpcSemconv; + } + + private static final Map rpcSystemNameMap = new HashMap<>(); + + static { + rpcSystemNameMap.put("apache_dubbo", "dubbo"); + rpcSystemNameMap.put("connect_rpc", "connectrpc"); + } + + public static String stableRpcSystemName(String oldRpcSystem) { + String rpcSystemName = rpcSystemNameMap.get(oldRpcSystem); + return rpcSystemName != null ? rpcSystemName : oldRpcSystem; + } + + public static AttributeKey getOldRpcMethodAttributeKey() { + if (emitStableRpcSemconv()) { + // to avoid clash when both semconv are emitted + return RPC_METHOD_OLD; + } + return RPC_METHOD; + } + private SemconvStability() {} + + public static Attributes getOldRpcMetricAttributes(Attributes attributes) { + if (emitStableRpcSemconv()) { + // need to copy attributes + return attributes.toBuilder().put(RPC_METHOD, attributes.get(RPC_METHOD_OLD)).build(); + } + return attributes; + } } diff --git a/instrumentation/apache-dubbo-2.7/javaagent/build.gradle.kts b/instrumentation/apache-dubbo-2.7/javaagent/build.gradle.kts index 0c68fb84f35e..4bd64a189c00 100644 --- a/instrumentation/apache-dubbo-2.7/javaagent/build.gradle.kts +++ b/instrumentation/apache-dubbo-2.7/javaagent/build.gradle.kts @@ -51,7 +51,13 @@ tasks.withType().configureEach { } tasks { + val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + jvmArgs("-Dotel.semconv-stability.opt-in=rpc") + } + check { - dependsOn(testing.suites) + dependsOn(testing.suites, testStableSemconv) } } diff --git a/instrumentation/apache-dubbo-2.7/library-autoconfigure/build.gradle.kts b/instrumentation/apache-dubbo-2.7/library-autoconfigure/build.gradle.kts index 0fba704c6ef4..34a028bd9873 100644 --- a/instrumentation/apache-dubbo-2.7/library-autoconfigure/build.gradle.kts +++ b/instrumentation/apache-dubbo-2.7/library-autoconfigure/build.gradle.kts @@ -13,7 +13,7 @@ dependencies { testLibrary("org.apache.dubbo:dubbo-config-api:2.7.0") } -tasks.withType().configureEach { +fun Test.configureTestTask() { systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") // to suppress non-fatal errors on jdk17 @@ -21,3 +21,21 @@ tasks.withType().configureEach { // required on jdk17 jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") } + +tasks.withType().configureEach { + configureTestTask() +} + +tasks { + val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + configureTestTask() + jvmArgs("-Dotel.semconv-stability.opt-in=rpc") + } + + check { + dependsOn(testStableSemconv) + } +} diff --git a/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTest.java b/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTest.java index 80958482ed53..284b2d961044 100644 --- a/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTest.java +++ b/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTest.java @@ -6,6 +6,11 @@ package io.opentelemetry.instrumentation.apachedubbo.v2_7; import static io.opentelemetry.instrumentation.testing.GlobalTraceUtil.runWithSpan; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.getClientDurationMetricName; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.getDurationUnit; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.getServerDurationMetricName; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; 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; @@ -15,19 +20,19 @@ import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; import static io.opentelemetry.semconv.incubating.PeerIncubatingAttributes.PEER_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService; import io.opentelemetry.instrumentation.apachedubbo.v2_7.impl.HelloServiceImpl; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; import io.opentelemetry.instrumentation.test.utils.PortUtils; import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import io.opentelemetry.semconv.incubating.RpcIncubatingAttributes; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import java.lang.reflect.Field; import java.net.InetAddress; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; @@ -92,6 +97,51 @@ ReferenceConfig convertReference(ReferenceConfig c return (ReferenceConfig) config; } + private List buildClientSpanAttributes(String service, String method) { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("apache_dubbo")); + attrs.addAll(rpcMethodAssertions(service, method)); + if (hasPeerService()) { + attrs.add(equalTo(PEER_SERVICE, "test-peer-service")); + } + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(satisfies(SERVER_PORT, k -> k.isInstanceOf(Long.class))); + attrs.add( + satisfies( + NETWORK_PEER_ADDRESS, k -> assertLatestDeps(k, a -> a.isInstanceOf(String.class)))); + attrs.add( + satisfies(NETWORK_PEER_PORT, k -> assertLatestDeps(k, a -> a.isInstanceOf(Long.class)))); + attrs.add(satisfies(NETWORK_TYPE, AbstractDubboTest::assertNetworkType)); + return attrs; + } + + private static List buildServerSpanAttributes(String service, String method) { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("apache_dubbo")); + attrs.addAll(rpcMethodAssertions(service, method)); + attrs.add(satisfies(NETWORK_PEER_ADDRESS, k -> k.isInstanceOf(String.class))); + attrs.add(satisfies(NETWORK_PEER_PORT, k -> k.isInstanceOf(Long.class))); + return attrs; + } + + private static List buildMetricAttributes(String service, String method) { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("apache_dubbo")); + attrs.addAll(rpcMethodAssertions(service, method)); + return attrs; + } + + private static List buildClientMetricAttributes( + String service, String method) { + List attrs = buildMetricAttributes(service, method); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(satisfies(SERVER_PORT, k -> k.isInstanceOf(Long.class))); + if (SemconvStability.emitOldRpcSemconv()) { + attrs.add(satisfies(NETWORK_TYPE, AbstractDubboTest::assertNetworkType)); + } + return attrs; + } + @Test void testApacheDubboBase() throws ReflectiveOperationException { int port = PortUtils.findOpenPort(); @@ -129,6 +179,7 @@ void testApacheDubboBase() throws ReflectiveOperationException { "hello", new String[] {String.class.getName()}, new Object[] {"hello"})); assertThat(response).isEqualTo("hello"); + testing() .waitAndAssertTraces( trace -> @@ -138,90 +189,54 @@ void testApacheDubboBase() throws ReflectiveOperationException { span.hasName("org.apache.dubbo.rpc.service.GenericService/$invoke") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes.RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo(RPC_SERVICE, "org.apache.dubbo.rpc.service.GenericService"), - equalTo(RPC_METHOD, "$invoke"), - equalTo( - PEER_SERVICE, hasPeerService() ? "test-peer-service" : null), - equalTo(SERVER_ADDRESS, "localhost"), - satisfies(SERVER_PORT, k -> k.isInstanceOf(Long.class)), - satisfies( - NETWORK_PEER_ADDRESS, - k -> assertLatestDeps(k, a -> a.isInstanceOf(String.class))), - satisfies( - NETWORK_PEER_PORT, - k -> assertLatestDeps(k, a -> a.isInstanceOf(Long.class))), - satisfies(NETWORK_TYPE, AbstractDubboTest::assertNetworkType)), + .hasAttributesSatisfying( + buildClientSpanAttributes( + "org.apache.dubbo.rpc.service.GenericService", "$invoke")), span -> span.hasName( "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService/hello") .hasKind(SpanKind.SERVER) .hasParent(trace.getSpan(1)) .hasAttributesSatisfying( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes.RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo( - RPC_SERVICE, - "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService"), - equalTo(RPC_METHOD, "hello"), - satisfies(NETWORK_PEER_ADDRESS, k -> k.isInstanceOf(String.class)), - satisfies(NETWORK_PEER_PORT, k -> k.isInstanceOf(Long.class))))); + buildServerSpanAttributes( + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService", + "hello")))); testing() .waitAndAssertMetrics( "io.opentelemetry.apache-dubbo-2.7", - "rpc.server.duration", + getServerDurationMetricName(), metrics -> metrics.anySatisfy( metric -> assertThat(metric) - .hasUnit("ms") + .hasUnit(getDurationUnit()) .hasHistogramSatisfying( histogram -> histogram.hasPointsSatisfying( point -> point.hasAttributesSatisfyingExactly( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes - .RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo( - RPC_SERVICE, - "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService"), - equalTo(RPC_METHOD, "hello")))))); + buildMetricAttributes( + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService", + "hello")))))); testing() .waitAndAssertMetrics( "io.opentelemetry.apache-dubbo-2.7", - "rpc.client.duration", + getClientDurationMetricName(), metrics -> metrics.anySatisfy( metric -> assertThat(metric) - .hasUnit("ms") + .hasUnit(getDurationUnit()) .hasHistogramSatisfying( histogram -> histogram.hasPointsSatisfying( point -> point.hasAttributesSatisfyingExactly( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes - .RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo( - RPC_SERVICE, - "org.apache.dubbo.rpc.service.GenericService"), - equalTo(RPC_METHOD, "$invoke"), - equalTo(SERVER_ADDRESS, "localhost"), - satisfies( - SERVER_PORT, k -> k.isInstanceOf(Long.class)), - satisfies( - NETWORK_TYPE, - AbstractDubboTest::assertNetworkType)))))); + buildClientMetricAttributes( + "org.apache.dubbo.rpc.service.GenericService", + "$invoke")))))); } @Test @@ -262,6 +277,23 @@ void testApacheDubboTest() assertThat(response.get()).isEqualTo("hello"); + // Build server span attributes with special network.type handling for this test + List attributes = + buildServerSpanAttributes( + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService", "hello"); + // this attribute is not filled reliably, it is either null or "ipv4"/"ipv6" + attributes.add( + satisfies( + NETWORK_TYPE, + k -> + assertLatestDeps( + k, + a -> + a.satisfiesAnyOf( + val -> assertThat(val).isNull(), + val -> assertThat(val).isEqualTo("ipv4"), + val -> assertThat(val).isEqualTo("ipv6"))))); + testing() .waitAndAssertTraces( trace -> @@ -271,102 +303,51 @@ void testApacheDubboTest() span.hasName("org.apache.dubbo.rpc.service.GenericService/$invokeAsync") .hasKind(SpanKind.CLIENT) .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes.RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo(RPC_SERVICE, "org.apache.dubbo.rpc.service.GenericService"), - equalTo(RPC_METHOD, "$invokeAsync"), - equalTo( - PEER_SERVICE, hasPeerService() ? "test-peer-service" : null), - equalTo(SERVER_ADDRESS, "localhost"), - satisfies(SERVER_PORT, k -> k.isInstanceOf(Long.class)), - satisfies( - NETWORK_PEER_ADDRESS, - k -> assertLatestDeps(k, a -> a.isInstanceOf(String.class))), - satisfies( - NETWORK_PEER_PORT, - k -> assertLatestDeps(k, a -> a.isInstanceOf(Long.class))), - satisfies(NETWORK_TYPE, AbstractDubboTest::assertNetworkType)), + .hasAttributesSatisfying( + buildClientSpanAttributes( + "org.apache.dubbo.rpc.service.GenericService", "$invokeAsync")), span -> span.hasName( "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService/hello") .hasKind(SpanKind.SERVER) .hasParent(trace.getSpan(1)) - .hasAttributesSatisfying( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes.RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo( - RPC_SERVICE, - "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService"), - equalTo(RPC_METHOD, "hello"), - satisfies(NETWORK_PEER_ADDRESS, k -> k.isInstanceOf(String.class)), - satisfies(NETWORK_PEER_PORT, k -> k.isInstanceOf(Long.class)), - // this attribute is not filled reliably, it is either null or - // "ipv4"/"ipv6" - satisfies( - NETWORK_TYPE, - k -> - assertLatestDeps( - k, - a -> - a.satisfiesAnyOf( - val -> assertThat(val).isNull(), - val -> assertThat(val).isEqualTo("ipv4"), - val -> assertThat(val).isEqualTo("ipv6"))))))); + .hasAttributesSatisfying(attributes))); testing() .waitAndAssertMetrics( "io.opentelemetry.apache-dubbo-2.7", - "rpc.server.duration", + getServerDurationMetricName(), metrics -> metrics.anySatisfy( metric -> assertThat(metric) - .hasUnit("ms") + .hasUnit(getDurationUnit()) .hasHistogramSatisfying( histogram -> histogram.hasPointsSatisfying( point -> point.hasAttributesSatisfyingExactly( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes - .RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo( - RPC_SERVICE, - "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService"), - equalTo(RPC_METHOD, "hello")))))); + buildMetricAttributes( + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService", + "hello")))))); testing() .waitAndAssertMetrics( "io.opentelemetry.apache-dubbo-2.7", - "rpc.client.duration", + getClientDurationMetricName(), metrics -> metrics.anySatisfy( metric -> assertThat(metric) - .hasUnit("ms") + .hasUnit(getDurationUnit()) .hasHistogramSatisfying( histogram -> histogram.hasPointsSatisfying( point -> point.hasAttributesSatisfyingExactly( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes - .RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo( - RPC_SERVICE, - "org.apache.dubbo.rpc.service.GenericService"), - equalTo(RPC_METHOD, "$invokeAsync"), - equalTo(SERVER_ADDRESS, "localhost"), - satisfies( - SERVER_PORT, k -> k.isInstanceOf(Long.class)), - satisfies( - NETWORK_TYPE, - AbstractDubboTest::assertNetworkType)))))); + buildClientMetricAttributes( + "org.apache.dubbo.rpc.service.GenericService", + "$invokeAsync")))))); } static void assertNetworkType(AbstractStringAssert stringAssert) { diff --git a/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTraceChainTest.java b/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTraceChainTest.java index 8c1700d87639..8fe8eab33321 100644 --- a/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTraceChainTest.java +++ b/instrumentation/apache-dubbo-2.7/testing/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/AbstractDubboTraceChainTest.java @@ -7,6 +7,11 @@ import static io.opentelemetry.instrumentation.apachedubbo.v2_7.AbstractDubboTest.assertLatestDeps; import static io.opentelemetry.instrumentation.testing.GlobalTraceUtil.runWithSpan; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.getClientDurationMetricName; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.getDurationUnit; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.getServerDurationMetricName; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; 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; @@ -16,21 +21,21 @@ import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; import static io.opentelemetry.semconv.incubating.PeerIncubatingAttributes.PEER_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService; import io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService; import io.opentelemetry.instrumentation.apachedubbo.v2_7.impl.HelloServiceImpl; import io.opentelemetry.instrumentation.apachedubbo.v2_7.impl.MiddleServiceImpl; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; import io.opentelemetry.instrumentation.test.utils.PortUtils; import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import io.opentelemetry.semconv.incubating.RpcIncubatingAttributes; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import java.lang.reflect.Field; import java.net.InetAddress; +import java.util.ArrayList; +import java.util.List; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; @@ -176,156 +181,166 @@ void testDubboChain() throws ReflectiveOperationException { trace -> trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), - span -> - span.hasName("org.apache.dubbo.rpc.service.GenericService/$invoke") - .hasKind(SpanKind.CLIENT) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes.RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo(RPC_SERVICE, "org.apache.dubbo.rpc.service.GenericService"), - equalTo(RPC_METHOD, "$invoke"), - equalTo( - PEER_SERVICE, hasPeerService() ? "test-peer-service" : null), - equalTo(SERVER_ADDRESS, "localhost"), - satisfies(SERVER_PORT, k -> k.isInstanceOf(Long.class)), - satisfies( - NETWORK_PEER_ADDRESS, - k -> assertLatestDeps(k, a -> a.isInstanceOf(String.class))), - satisfies( - NETWORK_PEER_PORT, - k -> assertLatestDeps(k, a -> a.isInstanceOf(Long.class))), - satisfies(NETWORK_TYPE, AbstractDubboTest::assertNetworkType)), - span -> - span.hasName( - "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService/hello") - .hasKind(SpanKind.SERVER) - .hasParent(trace.getSpan(1)) - .hasAttributesSatisfying( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes.RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo( - RPC_SERVICE, - "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService"), - equalTo(RPC_METHOD, "hello"), - satisfies(NETWORK_PEER_ADDRESS, k -> k.isInstanceOf(String.class)), - satisfies(NETWORK_PEER_PORT, k -> k.isInstanceOf(Long.class))), - span -> - span.hasName("org.apache.dubbo.rpc.service.GenericService/$invoke") - .hasKind(SpanKind.CLIENT) - .hasParent(trace.getSpan(2)) - .hasAttributesSatisfyingExactly( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes.RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo(RPC_SERVICE, "org.apache.dubbo.rpc.service.GenericService"), - equalTo(RPC_METHOD, "$invoke"), - equalTo( - PEER_SERVICE, hasPeerService() ? "test-peer-service" : null), - equalTo(SERVER_ADDRESS, "localhost"), - satisfies(SERVER_PORT, k -> k.isInstanceOf(Long.class)), - satisfies( - NETWORK_PEER_ADDRESS, - k -> assertLatestDeps(k, a -> a.isInstanceOf(String.class))), - satisfies( - NETWORK_PEER_PORT, - k -> assertLatestDeps(k, a -> a.isInstanceOf(Long.class))), - satisfies(NETWORK_TYPE, AbstractDubboTest::assertNetworkType)), - span -> - span.hasName( - "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService/hello") - .hasKind(SpanKind.SERVER) - .hasParent(trace.getSpan(3)) - .hasAttributesSatisfying( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes.RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo( - RPC_SERVICE, - "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService"), - equalTo(RPC_METHOD, "hello"), - satisfies(NETWORK_PEER_ADDRESS, k -> k.isInstanceOf(String.class)), - satisfies(NETWORK_PEER_PORT, k -> k.isInstanceOf(Long.class))))); + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("apache_dubbo")); + attrs.addAll( + rpcMethodAssertions( + "org.apache.dubbo.rpc.service.GenericService", "$invoke")); + attrs.add( + equalTo(PEER_SERVICE, hasPeerService() ? "test-peer-service" : null)); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(satisfies(SERVER_PORT, k -> k.isInstanceOf(Long.class))); + attrs.add( + satisfies( + NETWORK_PEER_ADDRESS, + k -> assertLatestDeps(k, a -> a.isInstanceOf(String.class)))); + attrs.add( + satisfies( + NETWORK_PEER_PORT, + k -> assertLatestDeps(k, a -> a.isInstanceOf(Long.class)))); + attrs.add(satisfies(NETWORK_TYPE, AbstractDubboTest::assertNetworkType)); + span.hasName("org.apache.dubbo.rpc.service.GenericService/$invoke") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(attrs); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("apache_dubbo")); + attrs.addAll( + rpcMethodAssertions( + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService", + "hello")); + attrs.add(satisfies(NETWORK_PEER_ADDRESS, k -> k.isInstanceOf(String.class))); + attrs.add(satisfies(NETWORK_PEER_PORT, k -> k.isInstanceOf(Long.class))); + span.hasName( + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService/hello") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfying(attrs); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("apache_dubbo")); + attrs.addAll( + rpcMethodAssertions( + "org.apache.dubbo.rpc.service.GenericService", "$invoke")); + attrs.add( + equalTo(PEER_SERVICE, hasPeerService() ? "test-peer-service" : null)); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(satisfies(SERVER_PORT, k -> k.isInstanceOf(Long.class))); + attrs.add( + satisfies( + NETWORK_PEER_ADDRESS, + k -> assertLatestDeps(k, a -> a.isInstanceOf(String.class)))); + attrs.add( + satisfies( + NETWORK_PEER_PORT, + k -> assertLatestDeps(k, a -> a.isInstanceOf(Long.class)))); + attrs.add(satisfies(NETWORK_TYPE, AbstractDubboTest::assertNetworkType)); + span.hasName("org.apache.dubbo.rpc.service.GenericService/$invoke") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(2)) + .hasAttributesSatisfyingExactly(attrs); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("apache_dubbo")); + attrs.addAll( + rpcMethodAssertions( + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService", + "hello")); + attrs.add(satisfies(NETWORK_PEER_ADDRESS, k -> k.isInstanceOf(String.class))); + attrs.add(satisfies(NETWORK_PEER_PORT, k -> k.isInstanceOf(Long.class))); + span.hasName( + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService/hello") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(3)) + .hasAttributesSatisfying(attrs); + })); testing() .waitAndAssertMetrics( "io.opentelemetry.apache-dubbo-2.7", - "rpc.server.duration", + getServerDurationMetricName(), metrics -> metrics.anySatisfy( metric -> assertThat(metric) - .hasUnit("ms") + .hasUnit(getDurationUnit()) .hasHistogramSatisfying( histogram -> histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfyingExactly( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes - .RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo( - RPC_SERVICE, - "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService"), - equalTo(RPC_METHOD, "hello")), - point -> - point.hasAttributesSatisfyingExactly( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes - .RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo( - RPC_SERVICE, - "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService"), - equalTo(RPC_METHOD, "hello")))))); + point -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("apache_dubbo")); + attrs.addAll( + rpcMethodAssertions( + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.HelloService", + "hello")); + point.hasAttributesSatisfyingExactly(attrs); + }, + point -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("apache_dubbo")); + attrs.addAll( + rpcMethodAssertions( + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService", + "hello")); + point.hasAttributesSatisfyingExactly(attrs); + })))); testing() .waitAndAssertMetrics( "io.opentelemetry.apache-dubbo-2.7", - "rpc.client.duration", + getClientDurationMetricName(), metrics -> metrics.anySatisfy( metric -> assertThat(metric) - .hasUnit("ms") + .hasUnit(getDurationUnit()) .hasHistogramSatisfying( histogram -> histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfyingExactly( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes - .RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo( - RPC_SERVICE, - "org.apache.dubbo.rpc.service.GenericService"), - equalTo(RPC_METHOD, "$invoke"), - equalTo(SERVER_ADDRESS, "localhost"), - satisfies( - SERVER_PORT, k -> k.isInstanceOf(Long.class)), + point -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("apache_dubbo")); + attrs.addAll( + rpcMethodAssertions( + "org.apache.dubbo.rpc.service.GenericService", + "$invoke")); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add( + satisfies( + SERVER_PORT, k -> k.isInstanceOf(Long.class))); + if (SemconvStability.emitOldRpcSemconv()) { + attrs.add( satisfies( NETWORK_TYPE, - AbstractDubboTest::assertNetworkType)), - point -> - point.hasAttributesSatisfyingExactly( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes - .RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo( - RPC_SERVICE, - "org.apache.dubbo.rpc.service.GenericService"), - equalTo(RPC_METHOD, "$invoke"), - equalTo(SERVER_ADDRESS, "localhost"), - satisfies( - SERVER_PORT, k -> k.isInstanceOf(Long.class)), + AbstractDubboTest::assertNetworkType)); + } + point.hasAttributesSatisfyingExactly(attrs); + }, + point -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("apache_dubbo")); + attrs.addAll( + rpcMethodAssertions( + "org.apache.dubbo.rpc.service.GenericService", + "$invoke")); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add( + satisfies( + SERVER_PORT, k -> k.isInstanceOf(Long.class))); + if (SemconvStability.emitOldRpcSemconv()) { + attrs.add( satisfies( NETWORK_TYPE, - AbstractDubboTest::assertNetworkType)))))); + AbstractDubboTest::assertNetworkType)); + } + point.hasAttributesSatisfyingExactly(attrs); + })))); } @Test @@ -377,93 +392,98 @@ void testDubboChainInJvm() throws ReflectiveOperationException { trace -> trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), - span -> - span.hasName("org.apache.dubbo.rpc.service.GenericService/$invoke") - .hasKind(SpanKind.CLIENT) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes.RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo(RPC_SERVICE, "org.apache.dubbo.rpc.service.GenericService"), - equalTo(RPC_METHOD, "$invoke"), - equalTo( - PEER_SERVICE, hasPeerService() ? "test-peer-service" : null), - equalTo(SERVER_ADDRESS, "localhost"), - satisfies(SERVER_PORT, k -> k.isInstanceOf(Long.class)), - satisfies( - NETWORK_PEER_ADDRESS, - k -> assertLatestDeps(k, a -> a.isInstanceOf(String.class))), - satisfies( - NETWORK_PEER_PORT, - k -> assertLatestDeps(k, a -> a.isInstanceOf(Long.class))), - satisfies(NETWORK_TYPE, AbstractDubboTest::assertNetworkType)), - span -> - span.hasName( - "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService/hello") - .hasKind(SpanKind.SERVER) - .hasParent(trace.getSpan(1)) - .hasAttributesSatisfying( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes.RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo( - RPC_SERVICE, - "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService"), - equalTo(RPC_METHOD, "hello"), - satisfies(NETWORK_PEER_ADDRESS, k -> k.isInstanceOf(String.class)), - satisfies(NETWORK_PEER_PORT, k -> k.isInstanceOf(Long.class))))); + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("apache_dubbo")); + attrs.addAll( + rpcMethodAssertions( + "org.apache.dubbo.rpc.service.GenericService", "$invoke")); + attrs.add( + equalTo(PEER_SERVICE, hasPeerService() ? "test-peer-service" : null)); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(satisfies(SERVER_PORT, k -> k.isInstanceOf(Long.class))); + attrs.add( + satisfies( + NETWORK_PEER_ADDRESS, + k -> assertLatestDeps(k, a -> a.isInstanceOf(String.class)))); + attrs.add( + satisfies( + NETWORK_PEER_PORT, + k -> assertLatestDeps(k, a -> a.isInstanceOf(Long.class)))); + attrs.add(satisfies(NETWORK_TYPE, AbstractDubboTest::assertNetworkType)); + span.hasName("org.apache.dubbo.rpc.service.GenericService/$invoke") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(attrs); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("apache_dubbo")); + attrs.addAll( + rpcMethodAssertions( + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService", + "hello")); + attrs.add(satisfies(NETWORK_PEER_ADDRESS, k -> k.isInstanceOf(String.class))); + attrs.add(satisfies(NETWORK_PEER_PORT, k -> k.isInstanceOf(Long.class))); + span.hasName( + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService/hello") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfying(attrs); + })); testing() .waitAndAssertMetrics( "io.opentelemetry.apache-dubbo-2.7", - "rpc.server.duration", + getServerDurationMetricName(), metrics -> metrics.anySatisfy( metric -> assertThat(metric) - .hasUnit("ms") + .hasUnit(getDurationUnit()) .hasHistogramSatisfying( histogram -> histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfyingExactly( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes - .RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo( - RPC_SERVICE, - "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService"), - equalTo(RPC_METHOD, "hello")))))); + point -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("apache_dubbo")); + attrs.addAll( + rpcMethodAssertions( + "io.opentelemetry.instrumentation.apachedubbo.v2_7.api.MiddleService", + "hello")); + point.hasAttributesSatisfyingExactly(attrs); + })))); testing() .waitAndAssertMetrics( "io.opentelemetry.apache-dubbo-2.7", - "rpc.client.duration", + getClientDurationMetricName(), metrics -> metrics.anySatisfy( metric -> assertThat(metric) - .hasUnit("ms") + .hasUnit(getDurationUnit()) .hasHistogramSatisfying( histogram -> histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfyingExactly( - equalTo( - RPC_SYSTEM, - RpcIncubatingAttributes - .RpcSystemIncubatingValues.APACHE_DUBBO), - equalTo( - RPC_SERVICE, - "org.apache.dubbo.rpc.service.GenericService"), - equalTo(RPC_METHOD, "$invoke"), - equalTo(SERVER_ADDRESS, "localhost"), - satisfies( - SERVER_PORT, k -> k.isInstanceOf(Long.class)), + point -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("apache_dubbo")); + attrs.addAll( + rpcMethodAssertions( + "org.apache.dubbo.rpc.service.GenericService", + "$invoke")); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add( + satisfies( + SERVER_PORT, k -> k.isInstanceOf(Long.class))); + if (SemconvStability.emitOldRpcSemconv()) { + attrs.add( satisfies( NETWORK_TYPE, - AbstractDubboTest::assertNetworkType)))))); + AbstractDubboTest::assertNetworkType)); + } + point.hasAttributesSatisfyingExactly(attrs); + })))); } } 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..986287083ac3 100644 --- a/instrumentation/armeria/armeria-grpc-1.14/javaagent/build.gradle.kts +++ b/instrumentation/armeria/armeria-grpc-1.14/javaagent/build.gradle.kts @@ -59,12 +59,25 @@ afterEvaluate { } } -tasks.test { - systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") -} +tasks { + test { + 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") + } + + check { + dependsOn(testStableSemconv) + } -if (findProperty("denyUnsafe") as Boolean) { - tasks.withType().configureEach { - enabled = false + if (findProperty("denyUnsafe") as Boolean) { + withType().configureEach { + enabled = false + } } } 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 e052719daf25..badc579af702 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,13 +5,12 @@ package io.opentelemetry.javaagent.instrumentation.armeria.grpc.v1_14; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.grpcStatusCodeAssertion; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; 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.RpcIncubatingAttributes.RPC_GRPC_STATUS_CODE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -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; import com.linecorp.armeria.client.grpc.GrpcClients; @@ -24,7 +23,10 @@ import io.grpc.stub.StreamObserver; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.semconv.incubating.MessageIncubatingAttributes; +import java.util.ArrayList; +import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -57,6 +59,17 @@ public void sayHello( } }; + private static List buildGrpcSpanAttributes( + String service, String method, long statusCode, String serverAddress, long serverPort) { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions(service, method)); + attrs.add(grpcStatusCodeAssertion(statusCode)); + attrs.add(equalTo(SERVER_ADDRESS, serverAddress)); + attrs.add(equalTo(SERVER_PORT, serverPort)); + return attrs; + } + @SuppressWarnings("deprecation") // using deprecated semconv @Test void grpcInstrumentation() { @@ -76,13 +89,13 @@ void grpcInstrumentation() { span.hasName("example.Greeter/SayHello") .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(SERVER_ADDRESS, "127.0.0.1"), - equalTo(SERVER_PORT, (long) server.httpPort())) + .hasAttributesSatisfying( + buildGrpcSpanAttributes( + "example.Greeter", + "SayHello", + Status.Code.OK.value(), + "127.0.0.1", + server.httpPort())) .hasEventsSatisfyingExactly( event -> event @@ -101,13 +114,13 @@ void grpcInstrumentation() { span.hasName("example.Greeter/SayHello") .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(SERVER_ADDRESS, "127.0.0.1"), - equalTo(SERVER_PORT, server.httpPort())) + .hasAttributesSatisfying( + buildGrpcSpanAttributes( + "example.Greeter", + "SayHello", + Status.Code.OK.value(), + "127.0.0.1", + server.httpPort())) .hasEventsSatisfyingExactly( event -> event diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/build.gradle.kts index c2e3939596b5..c1f5d28d5d4c 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/build.gradle.kts @@ -159,9 +159,9 @@ tasks { testClassesDirs = sourceSets.test.get().output.classesDirs classpath = sourceSets.test.get().runtimeClasspath - jvmArgs("-Dotel.semconv-stability.opt-in=database") + jvmArgs("-Dotel.semconv-stability.opt-in=rpc,database") systemProperty("collectMetadata", collectMetadata) - systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") + systemProperty("metadataConfig", "otel.semconv-stability.opt-in=rpc,database") } check { diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/AwsSpanAssertions.java b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/AwsSpanAssertions.java index 34b813c0256d..85beec3f434f 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/AwsSpanAssertions.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/AwsSpanAssertions.java @@ -6,6 +6,8 @@ package io.opentelemetry.javaagent.instrumentation.awssdk.v1_11; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; @@ -19,95 +21,105 @@ import static io.opentelemetry.semconv.incubating.AwsIncubatingAttributes.AWS_SNS_TOPIC_ARN; import static io.opentelemetry.semconv.incubating.AwsIncubatingAttributes.AWS_SQS_QUEUE_URL; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -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; import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; +import java.util.ArrayList; +import java.util.List; class AwsSpanAssertions { private AwsSpanAssertions() {} + @SuppressWarnings("deprecation") // using deprecated semconv static SpanDataAssert sqs( SpanDataAssert span, String queueName, String queueUrl, String rpcMethod) { + List attributes = new ArrayList<>(); + attributes.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + if (queueName != null) { + attributes.add(equalTo(stringKey("aws.queue.name"), queueName)); + } + if (queueUrl != null) { + attributes.add(equalTo(AWS_SQS_QUEUE_URL, queueUrl)); + } + attributes.add(satisfies(AWS_REQUEST_ID, v -> v.isInstanceOf(String.class))); + attributes.add(rpcSystemAssertion("aws-api")); + attributes.addAll(rpcMethodAssertions("AmazonSQS", rpcMethod)); + attributes.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attributes.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attributes.add(satisfies(URL_FULL, val -> val.startsWith("http://"))); + attributes.add(satisfies(SERVER_ADDRESS, v -> v.isInstanceOf(String.class))); + attributes.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + attributes.add( + satisfies( + SERVER_PORT, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), v -> assertThat(v).isInstanceOf(Number.class)))); + return span.hasName("SQS." + rpcMethod) .hasKind(SpanKind.CLIENT) .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(stringKey("aws.queue.name"), queueName), - equalTo(AWS_SQS_QUEUE_URL, queueUrl), - satisfies(AWS_REQUEST_ID, v -> v.isInstanceOf(String.class)), - equalTo(RPC_METHOD, rpcMethod), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - satisfies(URL_FULL, val -> val.startsWith("http://")), - satisfies(SERVER_ADDRESS, v -> v.isInstanceOf(String.class)), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1"), - satisfies( - SERVER_PORT, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(Number.class)))); + .hasAttributesSatisfyingExactly(attributes); } + @SuppressWarnings("deprecation") // using deprecated semconv static SpanDataAssert s3( SpanDataAssert span, String bucketName, String rpcMethod, String requestMethod, int responseStatusCode) { + List attributes = new ArrayList<>(); + attributes.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attributes.add(equalTo(AWS_S3_BUCKET, bucketName)); + attributes.add(rpcSystemAssertion("aws-api")); + attributes.addAll(rpcMethodAssertions("Amazon S3", rpcMethod)); + attributes.add(equalTo(HTTP_REQUEST_METHOD, requestMethod)); + attributes.add(equalTo(HTTP_RESPONSE_STATUS_CODE, responseStatusCode)); + attributes.add(satisfies(URL_FULL, val -> val.startsWith("http://"))); + attributes.add(satisfies(SERVER_ADDRESS, v -> v.isInstanceOf(String.class))); + attributes.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + attributes.add( + satisfies( + SERVER_PORT, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), v -> assertThat(v).isInstanceOf(Number.class)))); return span.hasName("S3." + rpcMethod) .hasKind(SpanKind.CLIENT) .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_S3_BUCKET, bucketName), - equalTo(RPC_METHOD, rpcMethod), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "Amazon S3"), - equalTo(HTTP_REQUEST_METHOD, requestMethod), - equalTo(HTTP_RESPONSE_STATUS_CODE, responseStatusCode), - satisfies(URL_FULL, val -> val.startsWith("http://")), - satisfies(SERVER_ADDRESS, v -> v.isInstanceOf(String.class)), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1"), - satisfies( - SERVER_PORT, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(Number.class)))); + .hasAttributesSatisfyingExactly(attributes); } + @SuppressWarnings("deprecation") // using deprecated semconv static SpanDataAssert sns(SpanDataAssert span, String topicArn, String rpcMethod) { + List attributes = new ArrayList<>(); + attributes.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + if (topicArn != null) { + attributes.add(equalTo(MESSAGING_DESTINATION_NAME, topicArn)); + } + attributes.add(satisfies(AWS_SNS_TOPIC_ARN, v -> v.isInstanceOf(String.class))); + attributes.add(satisfies(AWS_REQUEST_ID, v -> v.isInstanceOf(String.class))); + attributes.add(rpcSystemAssertion("aws-api")); + attributes.addAll(rpcMethodAssertions("AmazonSNS", rpcMethod)); + attributes.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attributes.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attributes.add(satisfies(URL_FULL, val -> val.startsWith("http://"))); + attributes.add(satisfies(SERVER_ADDRESS, v -> v.isInstanceOf(String.class))); + attributes.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + attributes.add( + satisfies( + SERVER_PORT, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), v -> assertThat(v).isInstanceOf(Number.class)))); return span.hasName("SNS." + rpcMethod) .hasKind(SpanKind.CLIENT) .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(MESSAGING_DESTINATION_NAME, topicArn), - satisfies(AWS_SNS_TOPIC_ARN, v -> v.isInstanceOf(String.class)), - satisfies(AWS_REQUEST_ID, v -> v.isInstanceOf(String.class)), - equalTo(RPC_METHOD, rpcMethod), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSNS"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - satisfies(URL_FULL, val -> val.startsWith("http://")), - satisfies(SERVER_ADDRESS, v -> v.isInstanceOf(String.class)), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1"), - satisfies( - SERVER_PORT, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(Number.class)))); + .hasAttributesSatisfyingExactly(attributes); } } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/S3ClientTest.java b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/S3ClientTest.java index 06704ead7548..3ae02769d35b 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/S3ClientTest.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/S3ClientTest.java @@ -7,15 +7,14 @@ import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.api.trace.SpanKind.CLIENT; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; import static io.opentelemetry.semconv.UrlAttributes.URL_FULL; import static io.opentelemetry.semconv.incubating.AwsIncubatingAttributes.AWS_S3_BUCKET; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -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; import static org.assertj.core.api.Assertions.catchThrowable; @@ -33,7 +32,9 @@ import io.opentelemetry.instrumentation.awssdk.v1_11.AbstractS3ClientTest; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.sdk.trace.data.StatusData; +import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.stream.Stream; @@ -119,22 +120,24 @@ public void beforeRequest(Request request) { .waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( - span -> - span.hasName("S3.HeadBucket") - .hasKind(CLIENT) - .hasStatus(StatusData.error()) - .hasException(caught) - .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(URL_FULL, "https://s3.amazonaws.com"), - equalTo(HTTP_REQUEST_METHOD, "HEAD"), - equalTo(SERVER_ADDRESS, "s3.amazonaws.com"), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "Amazon S3"), - equalTo(RPC_METHOD, "HeadBucket"), - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_S3_BUCKET, "someBucket"), - equalTo(ERROR_TYPE, IllegalStateException.class.getName())))); + span -> { + List attributes = new ArrayList<>(); + attributes.add(equalTo(URL_FULL, "https://s3.amazonaws.com")); + attributes.add(equalTo(HTTP_REQUEST_METHOD, "HEAD")); + attributes.add(equalTo(SERVER_ADDRESS, "s3.amazonaws.com")); + attributes.add(rpcSystemAssertion("aws-api")); + attributes.addAll(rpcMethodAssertions("Amazon S3", "HeadBucket")); + attributes.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attributes.add(equalTo(AWS_S3_BUCKET, "someBucket")); + attributes.add(equalTo(ERROR_TYPE, IllegalStateException.class.getName())); + + span.hasName("S3.HeadBucket") + .hasKind(CLIENT) + .hasStatus(StatusData.error()) + .hasException(caught) + .hasNoParent() + .hasAttributesSatisfyingExactly(attributes); + })); } @Test diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/S3TracingTest.java b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/S3TracingTest.java index ea2d8b19e3a8..02afda9398be 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/S3TracingTest.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/S3TracingTest.java @@ -6,6 +6,8 @@ package io.opentelemetry.javaagent.instrumentation.awssdk.v1_11; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; import static io.opentelemetry.javaagent.instrumentation.awssdk.v1_11.AwsSpanAssertions.s3; import static io.opentelemetry.javaagent.instrumentation.awssdk.v1_11.AwsSpanAssertions.sns; import static io.opentelemetry.javaagent.instrumentation.awssdk.v1_11.AwsSpanAssertions.sqs; @@ -24,9 +26,6 @@ import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MessagingSystemIncubatingValues.AWS_SQS; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -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; import com.amazonaws.services.sqs.model.ReceiveMessageResult; @@ -34,6 +33,9 @@ import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +import java.util.ArrayList; +import java.util.List; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -94,32 +96,36 @@ void testS3UploadTriggersSqsMessage() { trace -> trace.hasSpansSatisfyingExactly( span -> s3(span, bucketName, "PutObject", "PUT", 200), - span -> - span.hasName("s3ToSqsTestQueue process") - .hasKind(SpanKind.CONSUMER) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_SQS_QUEUE_URL, queueUrl), - satisfies(AWS_REQUEST_ID, v -> v.isInstanceOf(String.class)), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - satisfies(URL_FULL, val -> val.startsWith("http://")), - satisfies(SERVER_ADDRESS, v -> v.isInstanceOf(String.class)), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1"), - satisfies( - SERVER_PORT, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(Number.class))), - equalTo(MESSAGING_SYSTEM, AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "s3ToSqsTestQueue"), - equalTo(MESSAGING_OPERATION, "process"), - satisfies(MESSAGING_MESSAGE_ID, v -> v.isInstanceOf(String.class))), + span -> { + List attributes = new ArrayList<>(); + attributes.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attributes.add(equalTo(AWS_SQS_QUEUE_URL, queueUrl)); + attributes.add(satisfies(AWS_REQUEST_ID, v -> v.isInstanceOf(String.class))); + attributes.add(rpcSystemAssertion("aws-api")); + attributes.addAll(rpcMethodAssertions("AmazonSQS", "ReceiveMessage")); + attributes.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attributes.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attributes.add(satisfies(URL_FULL, val -> val.startsWith("http://"))); + attributes.add(satisfies(SERVER_ADDRESS, v -> v.isInstanceOf(String.class))); + attributes.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + attributes.add( + satisfies( + SERVER_PORT, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(Number.class)))); + attributes.add(equalTo(MESSAGING_SYSTEM, AWS_SQS)); + attributes.add(equalTo(MESSAGING_DESTINATION_NAME, "s3ToSqsTestQueue")); + attributes.add(equalTo(MESSAGING_OPERATION, "process")); + attributes.add( + satisfies(MESSAGING_MESSAGE_ID, v -> v.isInstanceOf(String.class))); + + span.hasName("s3ToSqsTestQueue process") + .hasKind(SpanKind.CONSUMER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(attributes); + }, span -> span.hasName("process child") .hasParent(trace.getSpan(1)) @@ -186,32 +192,36 @@ void testS3UploadTriggersSnsTopicNotificationThenCreatesSqsMessage() { trace.hasSpansSatisfyingExactly(span -> s3(span, bucketName, "PutObject", "PUT", 200)), trace -> trace.hasSpansSatisfyingExactly( - span -> - span.hasName("s3ToSnsToSqsTestQueue process") - .hasKind(SpanKind.CONSUMER) - .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_SQS_QUEUE_URL, queueUrl), - satisfies(AWS_REQUEST_ID, v -> v.isInstanceOf(String.class)), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - satisfies(URL_FULL, val -> val.startsWith("http://")), - satisfies(SERVER_ADDRESS, v -> v.isInstanceOf(String.class)), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1"), - satisfies( - SERVER_PORT, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(Number.class))), - equalTo(MESSAGING_SYSTEM, AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "s3ToSnsToSqsTestQueue"), - equalTo(MESSAGING_OPERATION, "process"), - satisfies(MESSAGING_MESSAGE_ID, v -> v.isInstanceOf(String.class))), + span -> { + List attributes = new ArrayList<>(); + attributes.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attributes.add(equalTo(AWS_SQS_QUEUE_URL, queueUrl)); + attributes.add(satisfies(AWS_REQUEST_ID, v -> v.isInstanceOf(String.class))); + attributes.add(rpcSystemAssertion("aws-api")); + attributes.addAll(rpcMethodAssertions("AmazonSQS", "ReceiveMessage")); + attributes.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attributes.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attributes.add(satisfies(URL_FULL, val -> val.startsWith("http://"))); + attributes.add(satisfies(SERVER_ADDRESS, v -> v.isInstanceOf(String.class))); + attributes.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + attributes.add( + satisfies( + SERVER_PORT, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(Number.class)))); + attributes.add(equalTo(MESSAGING_SYSTEM, AWS_SQS)); + attributes.add(equalTo(MESSAGING_DESTINATION_NAME, "s3ToSnsToSqsTestQueue")); + attributes.add(equalTo(MESSAGING_OPERATION, "process")); + attributes.add( + satisfies(MESSAGING_MESSAGE_ID, v -> v.isInstanceOf(String.class))); + + span.hasName("s3ToSnsToSqsTestQueue process") + .hasKind(SpanKind.CONSUMER) + .hasNoParent() + .hasAttributesSatisfyingExactly(attributes); + }, span -> span.hasName("process child") .hasParent(trace.getSpan(0)) diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/SnsTracingTest.java b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/SnsTracingTest.java index 08102b76a546..02f4f7cdbb55 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/SnsTracingTest.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/SnsTracingTest.java @@ -6,6 +6,8 @@ package io.opentelemetry.javaagent.instrumentation.awssdk.v1_11; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; import static io.opentelemetry.javaagent.instrumentation.awssdk.v1_11.AwsSpanAssertions.sns; import static io.opentelemetry.javaagent.instrumentation.awssdk.v1_11.AwsSpanAssertions.sqs; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; @@ -23,9 +25,6 @@ import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MessagingSystemIncubatingValues.AWS_SQS; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -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; import com.amazonaws.services.sqs.model.ReceiveMessageResult; @@ -33,6 +32,9 @@ import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +import java.util.ArrayList; +import java.util.List; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -75,38 +77,41 @@ void testSnsNotificationTriggersSqsMessageConsumedWithAwsSdk() { span -> sqs(span, null, queueUrl, "SetQueueAttributes")), trace -> trace.hasSpansSatisfyingExactly(span -> sns(span, null, "CreateTopic")), trace -> trace.hasSpansSatisfyingExactly(span -> sns(span, topicArn, "Subscribe")), - trace -> - trace.hasSpansSatisfyingExactly( - span -> sns(span, topicArn, "Publish"), - span -> - span.hasName("snsToSqsTestQueue process") - .hasKind(SpanKind.CONSUMER) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_SQS_QUEUE_URL, queueUrl), - satisfies(AWS_REQUEST_ID, v -> v.isInstanceOf(String.class)), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - satisfies(URL_FULL, val -> val.startsWith("http://")), - satisfies(SERVER_ADDRESS, v -> v.isInstanceOf(String.class)), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1"), - satisfies( - SERVER_PORT, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isNull(), - v -> assertThat(v).isInstanceOf(Number.class))), - equalTo(MESSAGING_SYSTEM, AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "snsToSqsTestQueue"), - equalTo(MESSAGING_OPERATION, "process"), - satisfies(MESSAGING_MESSAGE_ID, v -> v.isInstanceOf(String.class))), - span -> - span.hasName("process child") - .hasParent(trace.getSpan(1)) - .hasAttributes(Attributes.empty()))); + trace -> { + List processAttributes = new ArrayList<>(); + processAttributes.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + processAttributes.add(equalTo(AWS_SQS_QUEUE_URL, queueUrl)); + processAttributes.add(satisfies(AWS_REQUEST_ID, v -> v.isInstanceOf(String.class))); + processAttributes.add(rpcSystemAssertion("aws-api")); + processAttributes.addAll(rpcMethodAssertions("AmazonSQS", "ReceiveMessage")); + processAttributes.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + processAttributes.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + processAttributes.add(satisfies(URL_FULL, val -> val.startsWith("http://"))); + processAttributes.add(satisfies(SERVER_ADDRESS, v -> v.isInstanceOf(String.class))); + processAttributes.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + processAttributes.add( + satisfies( + SERVER_PORT, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isNull(), + v -> assertThat(v).isInstanceOf(Number.class)))); + processAttributes.add(equalTo(MESSAGING_SYSTEM, AWS_SQS)); + processAttributes.add(equalTo(MESSAGING_DESTINATION_NAME, "snsToSqsTestQueue")); + processAttributes.add(equalTo(MESSAGING_OPERATION, "process")); + processAttributes.add(satisfies(MESSAGING_MESSAGE_ID, v -> v.isInstanceOf(String.class))); + + trace.hasSpansSatisfyingExactly( + span -> sns(span, topicArn, "Publish"), + span -> + span.hasName("snsToSqsTestQueue process") + .hasKind(SpanKind.CONSUMER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(processAttributes), + span -> + span.hasName("process child") + .hasParent(trace.getSpan(1)) + .hasAttributes(Attributes.empty())); + }); } } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/Aws0ClientTest.java b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/Aws0ClientTest.java index 554886f6df70..e994ac616f1a 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/Aws0ClientTest.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test_before_1_11_106/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/Aws0ClientTest.java @@ -9,8 +9,9 @@ import static io.opentelemetry.api.trace.SpanKind.CLIENT; import static io.opentelemetry.api.trace.SpanKind.PRODUCER; import static io.opentelemetry.instrumentation.test.utils.PortUtils.UNUSABLE_PORT; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; 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.HttpAttributes.HTTP_REQUEST_METHOD; import static io.opentelemetry.semconv.HttpAttributes.HTTP_RESPONSE_STATUS_CODE; @@ -19,9 +20,6 @@ import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; import static io.opentelemetry.semconv.UrlAttributes.URL_FULL; import static io.opentelemetry.semconv.incubating.AwsIncubatingAttributes.AWS_S3_BUCKET; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; import static java.util.Arrays.asList; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; @@ -50,6 +48,7 @@ import io.opentelemetry.api.trace.Span; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil; import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.testing.internal.armeria.common.HttpResponse; @@ -216,11 +215,12 @@ void testSendRequestWithMockedResponse( equalTo(NETWORK_PROTOCOL_VERSION, "1.1"), equalTo(SERVER_PORT, server.httpPort()), equalTo(SERVER_ADDRESS, "127.0.0.1"), - equalTo(RPC_SYSTEM, "aws-api"), - satisfies(RPC_SERVICE, v -> v.contains(service)), - equalTo(RPC_METHOD, operation), + rpcSystemAssertion("aws-api"), equalTo(stringKey("aws.agent"), "java-aws-sdk"))); + attributes.addAll( + RpcSemconvStabilityUtil.rpcMethodAssertions( + service, operation, (a, expected) -> a.contains(expected))); additionalAttributes.forEach((k, v) -> attributes.add(equalTo(stringKey(k), v))); span.hasName(service + "." + operation) @@ -252,23 +252,30 @@ void testSendS3RequestToClosedPort() { testing.waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( - span -> - span.hasName("S3.GetObject") - .hasKind(CLIENT) - .hasStatus(StatusData.error()) - .hasException(caught) - .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(URL_FULL, "http://localhost:" + UNUSABLE_PORT), - equalTo(HTTP_REQUEST_METHOD, "GET"), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, 61), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "Amazon S3"), - equalTo(RPC_METHOD, "GetObject"), - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_S3_BUCKET, "someBucket"), - equalTo(ERROR_TYPE, AmazonClientException.class.getName())))); + span -> { + List attributes = + new ArrayList<>( + asList( + equalTo(URL_FULL, "http://localhost:" + UNUSABLE_PORT), + equalTo(HTTP_REQUEST_METHOD, "GET"), + equalTo(SERVER_ADDRESS, "localhost"), + equalTo(SERVER_PORT, 61), + rpcSystemAssertion("aws-api"), + equalTo(stringKey("aws.agent"), "java-aws-sdk"))); + + attributes.addAll(rpcMethodAssertions("Amazon S3", "GetObject")); + attributes.addAll( + asList( + equalTo(AWS_S3_BUCKET, "someBucket"), + equalTo(ERROR_TYPE, AmazonClientException.class.getName()))); + + span.hasName("S3.GetObject") + .hasKind(CLIENT) + .hasStatus(StatusData.error()) + .hasException(caught) + .hasNoParent() + .hasAttributesSatisfyingExactly(attributes); + })); } @Test @@ -290,22 +297,29 @@ public void beforeRequest(Request request) { testing.waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( - span -> - span.hasName("S3.GetObject") - .hasKind(CLIENT) - .hasStatus(StatusData.error()) - .hasException(caught) - .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(URL_FULL, "https://s3.amazonaws.com"), - equalTo(HTTP_REQUEST_METHOD, "GET"), - equalTo(SERVER_ADDRESS, "s3.amazonaws.com"), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "Amazon S3"), - equalTo(RPC_METHOD, "GetObject"), - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_S3_BUCKET, "someBucket"), - equalTo(ERROR_TYPE, IllegalStateException.class.getName())))); + span -> { + List attributes = + new ArrayList<>( + asList( + equalTo(URL_FULL, "https://s3.amazonaws.com"), + equalTo(HTTP_REQUEST_METHOD, "GET"), + equalTo(SERVER_ADDRESS, "s3.amazonaws.com"), + rpcSystemAssertion("aws-api"), + equalTo(stringKey("aws.agent"), "java-aws-sdk"))); + + attributes.addAll(rpcMethodAssertions("Amazon S3", "GetObject")); + attributes.addAll( + asList( + equalTo(AWS_S3_BUCKET, "someBucket"), + equalTo(ERROR_TYPE, IllegalStateException.class.getName()))); + + span.hasName("S3.GetObject") + .hasKind(CLIENT) + .hasStatus(StatusData.error()) + .hasException(caught) + .hasNoParent() + .hasAttributesSatisfyingExactly(attributes); + })); } @Test @@ -328,23 +342,30 @@ void testTimeoutAndRetryErrorsAreNotCaptured() { testing.waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( - span -> - span.hasName("S3.GetObject") - .hasKind(CLIENT) - .hasStatus(StatusData.error()) - .hasException(caught) - .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(URL_FULL, server.httpUri().toString()), - equalTo(HTTP_REQUEST_METHOD, "GET"), - equalTo(SERVER_PORT, server.httpPort()), - equalTo(SERVER_ADDRESS, "127.0.0.1"), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "Amazon S3"), - equalTo(RPC_METHOD, "GetObject"), - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_S3_BUCKET, "someBucket"), - equalTo(ERROR_TYPE, AmazonClientException.class.getName())))); + span -> { + List attributes = + new ArrayList<>( + asList( + equalTo(URL_FULL, server.httpUri().toString()), + equalTo(HTTP_REQUEST_METHOD, "GET"), + equalTo(SERVER_PORT, server.httpPort()), + equalTo(SERVER_ADDRESS, "127.0.0.1"), + rpcSystemAssertion("aws-api"), + equalTo(stringKey("aws.agent"), "java-aws-sdk"))); + + attributes.addAll(rpcMethodAssertions("Amazon S3", "GetObject")); + attributes.addAll( + asList( + equalTo(AWS_S3_BUCKET, "someBucket"), + equalTo(ERROR_TYPE, AmazonClientException.class.getName()))); + + span.hasName("S3.GetObject") + .hasKind(CLIENT) + .hasStatus(StatusData.error()) + .hasException(caught) + .hasNoParent() + .hasAttributesSatisfyingExactly(attributes); + })); } @Test diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library-autoconfigure/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-1.11/library-autoconfigure/build.gradle.kts index f210d0e79173..fc744bdd1d7b 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library-autoconfigure/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library-autoconfigure/build.gradle.kts @@ -46,8 +46,19 @@ tasks { jvmArgs("-Dotel.instrumentation.messaging.experimental.receive-telemetry.enabled=true") } + val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + filter { + excludeTestsMatching("SqsSuppressReceiveSpansTest") + } + jvmArgs("-Dotel.semconv-stability.opt-in=rpc") + jvmArgs("-Dotel.instrumentation.messaging.experimental.receive-telemetry.enabled=true") + } + check { - dependsOn(testReceiveSpansDisabled) + dependsOn(testReceiveSpansDisabled, testStableSemconv) } } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts index 1ccae1d21c7a..7dee89b2805b 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts @@ -55,7 +55,7 @@ tasks { val testStableSemconv by registering(Test::class) { testClassesDirs = sourceSets.test.get().output.classesDirs classpath = sourceSets.test.get().runtimeClasspath - jvmArgs("-Dotel.semconv-stability.opt-in=database") + jvmArgs("-Dotel.semconv-stability.opt-in=rpc,database") } check { diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractBaseAwsClientTest.java b/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractBaseAwsClientTest.java index 1db080b80b15..658daef0da93 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractBaseAwsClientTest.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractBaseAwsClientTest.java @@ -8,6 +8,8 @@ import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.api.trace.SpanKind.CLIENT; import static io.opentelemetry.api.trace.SpanKind.PRODUCER; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; @@ -16,9 +18,6 @@ import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; import static io.opentelemetry.semconv.UrlAttributes.URL_FULL; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; @@ -100,11 +99,13 @@ public void assertRequestWithMockedResponse( equalTo(NETWORK_PROTOCOL_VERSION, "1.1"), equalTo(SERVER_PORT, server.httpPort()), equalTo(SERVER_ADDRESS, "127.0.0.1"), - equalTo(RPC_SYSTEM, "aws-api"), - satisfies(RPC_SERVICE, v -> v.contains(service)), - equalTo(RPC_METHOD, operation), + rpcSystemAssertion("aws-api"), equalTo(stringKey("aws.agent"), "java-aws-sdk"))); + attributes.addAll( + rpcMethodAssertions( + service, operation, (a, expected) -> a.contains(expected))); + if (hasRequestId()) { attributes.add( satisfies( diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractS3ClientTest.java b/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractS3ClientTest.java index 1f378a4a5176..9d31a6969dbd 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractS3ClientTest.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractS3ClientTest.java @@ -8,6 +8,8 @@ import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.api.trace.SpanKind.CLIENT; import static io.opentelemetry.instrumentation.test.utils.PortUtils.UNUSABLE_PORT; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; @@ -15,9 +17,6 @@ import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; import static io.opentelemetry.semconv.UrlAttributes.URL_FULL; import static io.opentelemetry.semconv.incubating.AwsIncubatingAttributes.AWS_S3_BUCKET; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; @@ -36,6 +35,7 @@ import io.opentelemetry.testing.internal.armeria.common.HttpStatus; import io.opentelemetry.testing.internal.armeria.common.MediaType; import java.time.Duration; +import java.util.ArrayList; import java.util.List; import java.util.function.Function; import java.util.stream.Stream; @@ -111,6 +111,17 @@ public void testSendRequestToClosedPort() { Throwable caught = catchThrowable(() -> client.getObject("someBucket", "someKey")); assertThat(caught).isInstanceOf(SdkClientException.class); + List attrs = new ArrayList<>(); + attrs.add(equalTo(URL_FULL, "http://127.0.0.1:" + UNUSABLE_PORT)); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "GET")); + attrs.add(equalTo(SERVER_ADDRESS, "127.0.0.1")); + attrs.add(equalTo(SERVER_PORT, 61)); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("Amazon S3", "GetObject")); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add(equalTo(AWS_S3_BUCKET, "someBucket")); + attrs.add(equalTo(ERROR_TYPE, SdkClientException.class.getName())); + testing() .waitAndAssertTraces( trace -> @@ -121,17 +132,7 @@ public void testSendRequestToClosedPort() { .hasStatus(StatusData.error()) .hasException(caught) .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(URL_FULL, "http://127.0.0.1:" + UNUSABLE_PORT), - equalTo(HTTP_REQUEST_METHOD, "GET"), - equalTo(SERVER_ADDRESS, "127.0.0.1"), - equalTo(SERVER_PORT, 61), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "Amazon S3"), - equalTo(RPC_METHOD, "GetObject"), - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_S3_BUCKET, "someBucket"), - equalTo(ERROR_TYPE, SdkClientException.class.getName())))); + .hasAttributesSatisfyingExactly(attrs))); } @Test @@ -154,6 +155,17 @@ void testTimeoutAndRetryErrorsNotCaptured() { assertThat(caught).isInstanceOf(AmazonClientException.class); assertThat(Span.current().getSpanContext().isValid()).isFalse(); + List attrs = new ArrayList<>(); + attrs.add(equalTo(URL_FULL, server.httpUri().toString())); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "GET")); + attrs.add(equalTo(SERVER_PORT, server.httpPort())); + attrs.add(equalTo(SERVER_ADDRESS, "127.0.0.1")); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("Amazon S3", "GetObject")); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add(equalTo(AWS_S3_BUCKET, "someBucket")); + attrs.add(equalTo(ERROR_TYPE, SdkClientException.class.getName())); + testing() .waitAndAssertTraces( trace -> @@ -166,16 +178,6 @@ void testTimeoutAndRetryErrorsNotCaptured() { .hasException( new SdkClientException( "Unable to execute HTTP request: Request did not complete before the request timeout configuration.")) - .hasAttributesSatisfyingExactly( - equalTo(URL_FULL, server.httpUri().toString()), - equalTo(HTTP_REQUEST_METHOD, "GET"), - equalTo(SERVER_PORT, server.httpPort()), - equalTo(SERVER_ADDRESS, "127.0.0.1"), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "Amazon S3"), - equalTo(RPC_METHOD, "GetObject"), - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_S3_BUCKET, "someBucket"), - equalTo(ERROR_TYPE, SdkClientException.class.getName())))); + .hasAttributesSatisfyingExactly(attrs))); } } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSqsSuppressReceiveSpansTest.java b/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSqsSuppressReceiveSpansTest.java index 962a5d3bd5c6..152540ed7e55 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSqsSuppressReceiveSpansTest.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSqsSuppressReceiveSpansTest.java @@ -6,6 +6,8 @@ package io.opentelemetry.instrumentation.awssdk.v1_11; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; @@ -20,9 +22,6 @@ import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_MESSAGE_ID; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -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; import com.amazonaws.auth.AWSStaticCredentialsProvider; @@ -38,8 +37,11 @@ import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.test.utils.PortUtils; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; import org.elasticmq.rest.sqs.SQSRestServer; import org.elasticmq.rest.sqs.SQSRestServerBuilder; import org.junit.jupiter.api.AfterEach; @@ -100,79 +102,86 @@ void testSimpleSqsProducerConsumerServices() { .waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( - span -> - span.hasName("SQS.CreateQueue") - .hasKind(SpanKind.CLIENT) - .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(stringKey("aws.queue.name"), "testSdkSqs"), - satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class)), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(RPC_METHOD, "CreateQueue"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(URL_FULL, "http://localhost:" + sqsPort), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1"))), + span -> { + List attrs = new ArrayList<>(); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add(equalTo(stringKey("aws.queue.name"), "testSdkSqs")); + attrs.add(satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("AmazonSQS", "CreateQueue")); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add(equalTo(URL_FULL, "http://localhost:" + sqsPort)); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, sqsPort)); + attrs.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + span.hasName("SQS.CreateQueue") + .hasKind(SpanKind.CLIENT) + .hasNoParent() + .hasAttributesSatisfyingExactly(attrs); + }), trace -> trace.hasSpansSatisfyingExactly( - span -> - span.hasName("testSdkSqs publish") - .hasKind(SpanKind.PRODUCER) - .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo( - AWS_SQS_QUEUE_URL, - "http://localhost:" + sqsPort + "/000000000000/testSdkSqs"), - satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class)), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(RPC_METHOD, "SendMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(URL_FULL, "http://localhost:" + sqsPort), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo( - MESSAGING_SYSTEM, - MessagingIncubatingAttributes.MessagingSystemIncubatingValues - .AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs"), - equalTo(MESSAGING_OPERATION, "publish"), - satisfies( - MESSAGING_MESSAGE_ID, val -> val.isInstanceOf(String.class)), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1")), - span -> - span.hasName("testSdkSqs process") - .hasKind(SpanKind.CONSUMER) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo( - AWS_SQS_QUEUE_URL, - "http://localhost:" + sqsPort + "/000000000000/testSdkSqs"), - satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class)), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(URL_FULL, "http://localhost:" + sqsPort), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo( - MESSAGING_SYSTEM, - MessagingIncubatingAttributes.MessagingSystemIncubatingValues - .AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs"), - equalTo(MESSAGING_OPERATION, "process"), - satisfies( - MESSAGING_MESSAGE_ID, val -> val.isInstanceOf(String.class)), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1")), + span -> { + List attrs = new ArrayList<>(); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add( + equalTo( + AWS_SQS_QUEUE_URL, + "http://localhost:" + sqsPort + "/000000000000/testSdkSqs")); + attrs.add(satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("AmazonSQS", "SendMessage")); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add(equalTo(URL_FULL, "http://localhost:" + sqsPort)); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, sqsPort)); + attrs.add( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues + .AWS_SQS)); + attrs.add(equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs")); + attrs.add(equalTo(MESSAGING_OPERATION, "publish")); + attrs.add( + satisfies(MESSAGING_MESSAGE_ID, val -> val.isInstanceOf(String.class))); + attrs.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + span.hasName("testSdkSqs publish") + .hasKind(SpanKind.PRODUCER) + .hasNoParent() + .hasAttributesSatisfyingExactly(attrs); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add( + equalTo( + AWS_SQS_QUEUE_URL, + "http://localhost:" + sqsPort + "/000000000000/testSdkSqs")); + attrs.add(satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("AmazonSQS", "ReceiveMessage")); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add(equalTo(URL_FULL, "http://localhost:" + sqsPort)); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, sqsPort)); + attrs.add( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues + .AWS_SQS)); + attrs.add(equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs")); + attrs.add(equalTo(MESSAGING_OPERATION, "process")); + attrs.add( + satisfies(MESSAGING_MESSAGE_ID, val -> val.isInstanceOf(String.class))); + attrs.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + span.hasName("testSdkSqs process") + .hasKind(SpanKind.CONSUMER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(attrs); + }, span -> span.hasName("process child") .hasParent(trace.getSpan(1)) @@ -203,79 +212,86 @@ void testSimpleSqsProducerConsumerServicesWithParentSpan() { .waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( - span -> - span.hasName("SQS.CreateQueue") - .hasKind(SpanKind.CLIENT) - .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(stringKey("aws.queue.name"), "testSdkSqs"), - satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class)), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(RPC_METHOD, "CreateQueue"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(URL_FULL, "http://localhost:" + sqsPort), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1"))), + span -> { + List attrs = new ArrayList<>(); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add(equalTo(stringKey("aws.queue.name"), "testSdkSqs")); + attrs.add(satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("AmazonSQS", "CreateQueue")); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add(equalTo(URL_FULL, "http://localhost:" + sqsPort)); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, sqsPort)); + attrs.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + span.hasName("SQS.CreateQueue") + .hasKind(SpanKind.CLIENT) + .hasNoParent() + .hasAttributesSatisfyingExactly(attrs); + }), trace -> trace.hasSpansSatisfyingExactly( - span -> - span.hasName("testSdkSqs publish") - .hasKind(SpanKind.PRODUCER) - .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo( - AWS_SQS_QUEUE_URL, - "http://localhost:" + sqsPort + "/000000000000/testSdkSqs"), - satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class)), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(RPC_METHOD, "SendMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(URL_FULL, "http://localhost:" + sqsPort), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo( - MESSAGING_SYSTEM, - MessagingIncubatingAttributes.MessagingSystemIncubatingValues - .AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs"), - equalTo(MESSAGING_OPERATION, "publish"), - satisfies( - MESSAGING_MESSAGE_ID, val -> val.isInstanceOf(String.class)), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1")), - span -> - span.hasName("testSdkSqs process") - .hasKind(SpanKind.CONSUMER) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo( - AWS_SQS_QUEUE_URL, - "http://localhost:" + sqsPort + "/000000000000/testSdkSqs"), - satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class)), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(URL_FULL, "http://localhost:" + sqsPort), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo( - MESSAGING_SYSTEM, - MessagingIncubatingAttributes.MessagingSystemIncubatingValues - .AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs"), - equalTo(MESSAGING_OPERATION, "process"), - satisfies( - MESSAGING_MESSAGE_ID, val -> val.isInstanceOf(String.class)), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1")), + span -> { + List attrs = new ArrayList<>(); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add( + equalTo( + AWS_SQS_QUEUE_URL, + "http://localhost:" + sqsPort + "/000000000000/testSdkSqs")); + attrs.add(satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("AmazonSQS", "SendMessage")); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add(equalTo(URL_FULL, "http://localhost:" + sqsPort)); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, sqsPort)); + attrs.add( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues + .AWS_SQS)); + attrs.add(equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs")); + attrs.add(equalTo(MESSAGING_OPERATION, "publish")); + attrs.add( + satisfies(MESSAGING_MESSAGE_ID, val -> val.isInstanceOf(String.class))); + attrs.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + span.hasName("testSdkSqs publish") + .hasKind(SpanKind.PRODUCER) + .hasNoParent() + .hasAttributesSatisfyingExactly(attrs); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add( + equalTo( + AWS_SQS_QUEUE_URL, + "http://localhost:" + sqsPort + "/000000000000/testSdkSqs")); + attrs.add(satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("AmazonSQS", "ReceiveMessage")); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add(equalTo(URL_FULL, "http://localhost:" + sqsPort)); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, sqsPort)); + attrs.add( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues + .AWS_SQS)); + attrs.add(equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs")); + attrs.add(equalTo(MESSAGING_OPERATION, "process")); + attrs.add( + satisfies(MESSAGING_MESSAGE_ID, val -> val.isInstanceOf(String.class))); + attrs.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + span.hasName("testSdkSqs process") + .hasKind(SpanKind.CONSUMER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(attrs); + }, span -> span.hasName("process child") .hasParent(trace.getSpan(1)) @@ -287,25 +303,27 @@ void testSimpleSqsProducerConsumerServicesWithParentSpan() { trace -> trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasNoParent(), - span -> - span.hasName("SQS.ReceiveMessage") - .hasKind(SpanKind.CLIENT) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo( - AWS_SQS_QUEUE_URL, - "http://localhost:" + sqsPort + "/000000000000/testSdkSqs"), - satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class)), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(URL_FULL, "http://localhost:" + sqsPort), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1")))); + span -> { + List attrs = new ArrayList<>(); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add( + equalTo( + AWS_SQS_QUEUE_URL, + "http://localhost:" + sqsPort + "/000000000000/testSdkSqs")); + attrs.add(satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("AmazonSQS", "ReceiveMessage")); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add(equalTo(URL_FULL, "http://localhost:" + sqsPort)); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, sqsPort)); + attrs.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + span.hasName("SQS.ReceiveMessage") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(attrs); + })); } @Test diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSqsTracingTest.java b/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSqsTracingTest.java index 42a44f380937..0b8ef7ab4c2c 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSqsTracingTest.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AbstractSqsTracingTest.java @@ -7,6 +7,8 @@ import static io.opentelemetry.api.common.AttributeKey.stringArrayKey; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; @@ -22,9 +24,6 @@ import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_MESSAGE_ID; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; @@ -134,51 +133,53 @@ void testSimpleSqsProducerConsumerServicesCaptureHeaders(boolean testCaptureHead .waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( - span -> - span.hasName("SQS.CreateQueue") - .hasKind(SpanKind.CLIENT) - .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(stringKey("aws.queue.name"), "testSdkSqs"), - satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class)), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(RPC_METHOD, "CreateQueue"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(URL_FULL, "http://localhost:" + sqsPort), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1"))), + span -> { + List attributes = new ArrayList<>(); + attributes.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attributes.add(equalTo(stringKey("aws.queue.name"), "testSdkSqs")); + attributes.add( + satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + attributes.add(rpcSystemAssertion("aws-api")); + attributes.addAll(rpcMethodAssertions("AmazonSQS", "CreateQueue")); + attributes.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attributes.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attributes.add(equalTo(URL_FULL, "http://localhost:" + sqsPort)); + attributes.add(equalTo(SERVER_ADDRESS, "localhost")); + attributes.add(equalTo(SERVER_PORT, sqsPort)); + attributes.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + span.hasName("SQS.CreateQueue") + .hasKind(SpanKind.CLIENT) + .hasNoParent() + .hasAttributesSatisfyingExactly(attributes); + }), trace -> trace.hasSpansSatisfyingExactly( span -> { - List attributes = - new ArrayList<>( - Arrays.asList( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo( - AWS_SQS_QUEUE_URL, - "http://localhost:" + sqsPort + "/000000000000/testSdkSqs"), - satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class)), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(RPC_METHOD, "SendMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(URL_FULL, "http://localhost:" + sqsPort), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo( - MESSAGING_SYSTEM, - MessagingIncubatingAttributes.MessagingSystemIncubatingValues - .AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs"), - equalTo(MESSAGING_OPERATION, "publish"), - satisfies( - MESSAGING_MESSAGE_ID, val -> val.isInstanceOf(String.class)), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1"))); + List attributes = new ArrayList<>(); + attributes.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attributes.add( + equalTo( + AWS_SQS_QUEUE_URL, + "http://localhost:" + sqsPort + "/000000000000/testSdkSqs")); + attributes.add( + satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + attributes.add(rpcSystemAssertion("aws-api")); + attributes.addAll(rpcMethodAssertions("AmazonSQS", "SendMessage")); + attributes.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attributes.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attributes.add(equalTo(URL_FULL, "http://localhost:" + sqsPort)); + attributes.add(equalTo(SERVER_ADDRESS, "localhost")); + attributes.add(equalTo(SERVER_PORT, sqsPort)); + attributes.add( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues + .AWS_SQS)); + attributes.add(equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs")); + attributes.add(equalTo(MESSAGING_OPERATION, "publish")); + attributes.add( + satisfies(MESSAGING_MESSAGE_ID, val -> val.isInstanceOf(String.class))); + attributes.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); if (testCaptureHeaders) { attributes.add( @@ -195,30 +196,30 @@ void testSimpleSqsProducerConsumerServicesCaptureHeaders(boolean testCaptureHead trace -> trace.hasSpansSatisfyingExactly( span -> { - List attributes = - new ArrayList<>( - Arrays.asList( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo( - AWS_SQS_QUEUE_URL, - "http://localhost:" + sqsPort + "/000000000000/testSdkSqs"), - satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class)), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(URL_FULL, "http://localhost:" + sqsPort), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo( - MESSAGING_SYSTEM, - MessagingIncubatingAttributes.MessagingSystemIncubatingValues - .AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs"), - equalTo(MESSAGING_OPERATION, "receive"), - equalTo(MESSAGING_BATCH_MESSAGE_COUNT, 1), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1"))); + List attributes = new ArrayList<>(); + attributes.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attributes.add( + equalTo( + AWS_SQS_QUEUE_URL, + "http://localhost:" + sqsPort + "/000000000000/testSdkSqs")); + attributes.add( + satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + attributes.add(rpcSystemAssertion("aws-api")); + attributes.addAll(rpcMethodAssertions("AmazonSQS", "ReceiveMessage")); + attributes.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attributes.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attributes.add(equalTo(URL_FULL, "http://localhost:" + sqsPort)); + attributes.add(equalTo(SERVER_ADDRESS, "localhost")); + attributes.add(equalTo(SERVER_PORT, sqsPort)); + attributes.add( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues + .AWS_SQS)); + attributes.add(equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs")); + attributes.add(equalTo(MESSAGING_OPERATION, "receive")); + attributes.add(equalTo(MESSAGING_BATCH_MESSAGE_COUNT, 1)); + attributes.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); if (testCaptureHeaders) { attributes.add( @@ -233,31 +234,31 @@ void testSimpleSqsProducerConsumerServicesCaptureHeaders(boolean testCaptureHead .hasAttributesSatisfyingExactly(attributes); }, span -> { - List attributes = - new ArrayList<>( - Arrays.asList( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo( - AWS_SQS_QUEUE_URL, - "http://localhost:" + sqsPort + "/000000000000/testSdkSqs"), - satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class)), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(URL_FULL, "http://localhost:" + sqsPort), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo( - MESSAGING_SYSTEM, - MessagingIncubatingAttributes.MessagingSystemIncubatingValues - .AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs"), - equalTo(MESSAGING_OPERATION, "process"), - satisfies( - MESSAGING_MESSAGE_ID, val -> val.isInstanceOf(String.class)), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1"))); + List attributes = new ArrayList<>(); + attributes.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attributes.add( + equalTo( + AWS_SQS_QUEUE_URL, + "http://localhost:" + sqsPort + "/000000000000/testSdkSqs")); + attributes.add( + satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + attributes.add(rpcSystemAssertion("aws-api")); + attributes.addAll(rpcMethodAssertions("AmazonSQS", "ReceiveMessage")); + attributes.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attributes.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attributes.add(equalTo(URL_FULL, "http://localhost:" + sqsPort)); + attributes.add(equalTo(SERVER_ADDRESS, "localhost")); + attributes.add(equalTo(SERVER_PORT, sqsPort)); + attributes.add( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues + .AWS_SQS)); + attributes.add(equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs")); + attributes.add(equalTo(MESSAGING_OPERATION, "process")); + attributes.add( + satisfies(MESSAGING_MESSAGE_ID, val -> val.isInstanceOf(String.class))); + attributes.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); if (testCaptureHeaders) { attributes.add( @@ -300,52 +301,58 @@ void testSimpleSqsProducerConsumerServicesWithParentSpan() { .waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( - span -> - span.hasName("SQS.CreateQueue") - .hasKind(SpanKind.CLIENT) - .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(stringKey("aws.queue.name"), "testSdkSqs"), - satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class)), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(RPC_METHOD, "CreateQueue"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(URL_FULL, "http://localhost:" + sqsPort), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1"))), + span -> { + List attributes = new ArrayList<>(); + attributes.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attributes.add(equalTo(stringKey("aws.queue.name"), "testSdkSqs")); + attributes.add( + satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + attributes.add(rpcSystemAssertion("aws-api")); + attributes.addAll(rpcMethodAssertions("AmazonSQS", "CreateQueue")); + attributes.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attributes.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attributes.add(equalTo(URL_FULL, "http://localhost:" + sqsPort)); + attributes.add(equalTo(SERVER_ADDRESS, "localhost")); + attributes.add(equalTo(SERVER_PORT, sqsPort)); + attributes.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + span.hasName("SQS.CreateQueue") + .hasKind(SpanKind.CLIENT) + .hasNoParent() + .hasAttributesSatisfyingExactly(attributes); + }), trace -> trace.hasSpansSatisfyingExactly( - span -> - span.hasName("testSdkSqs publish") - .hasKind(SpanKind.PRODUCER) - .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo( - AWS_SQS_QUEUE_URL, - "http://localhost:" + sqsPort + "/000000000000/testSdkSqs"), - satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class)), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(RPC_METHOD, "SendMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(URL_FULL, "http://localhost:" + sqsPort), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo( - MESSAGING_SYSTEM, - MessagingIncubatingAttributes.MessagingSystemIncubatingValues - .AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs"), - equalTo(MESSAGING_OPERATION, "publish"), - satisfies( - MESSAGING_MESSAGE_ID, val -> val.isInstanceOf(String.class)), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1"))), + span -> { + List attributes = new ArrayList<>(); + attributes.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attributes.add( + equalTo( + AWS_SQS_QUEUE_URL, + "http://localhost:" + sqsPort + "/000000000000/testSdkSqs")); + attributes.add( + satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + attributes.add(rpcSystemAssertion("aws-api")); + attributes.addAll(rpcMethodAssertions("AmazonSQS", "SendMessage")); + attributes.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attributes.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attributes.add(equalTo(URL_FULL, "http://localhost:" + sqsPort)); + attributes.add(equalTo(SERVER_ADDRESS, "localhost")); + attributes.add(equalTo(SERVER_PORT, sqsPort)); + attributes.add( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues + .AWS_SQS)); + attributes.add(equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs")); + attributes.add(equalTo(MESSAGING_OPERATION, "publish")); + attributes.add( + satisfies(MESSAGING_MESSAGE_ID, val -> val.isInstanceOf(String.class))); + attributes.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + span.hasName("testSdkSqs publish") + .hasKind(SpanKind.PRODUCER) + .hasNoParent() + .hasAttributesSatisfyingExactly(attributes); + }), trace -> { AtomicReference receiveSpan = new AtomicReference<>(); AtomicReference processSpan = new AtomicReference<>(); @@ -357,91 +364,93 @@ void testSimpleSqsProducerConsumerServicesWithParentSpan() { span.hasName("parent") .hasNoParent() .hasAttributes(Attributes.empty()), - span -> - span.hasName("SQS.ReceiveMessage") - .hasKind(SpanKind.CLIENT) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo( - AWS_SQS_QUEUE_URL, - "http://localhost:" - + sqsPort - + "/000000000000/testSdkSqs"), - satisfies( - AWS_REQUEST_ID, val -> val.isInstanceOf(String.class)), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(URL_FULL, "http://localhost:" + sqsPort), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1")), - span -> - span.hasName("testSdkSqs receive") - .hasKind(SpanKind.CONSUMER) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo( - AWS_SQS_QUEUE_URL, - "http://localhost:" - + sqsPort - + "/000000000000/testSdkSqs"), - satisfies( - AWS_REQUEST_ID, val -> val.isInstanceOf(String.class)), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(URL_FULL, "http://localhost:" + sqsPort), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo( - MESSAGING_SYSTEM, - MessagingIncubatingAttributes - .MessagingSystemIncubatingValues.AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs"), - equalTo(MESSAGING_OPERATION, "receive"), - equalTo( - MessagingIncubatingAttributes - .MESSAGING_BATCH_MESSAGE_COUNT, - 1), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1")), - span -> - span.hasName("testSdkSqs process") - .hasKind(SpanKind.CONSUMER) - .hasParent(receiveSpan.get()) - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo( - AWS_SQS_QUEUE_URL, - "http://localhost:" - + sqsPort - + "/000000000000/testSdkSqs"), - satisfies( - AWS_REQUEST_ID, val -> val.isInstanceOf(String.class)), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "AmazonSQS"), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(URL_FULL, "http://localhost:" + sqsPort), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo( - MESSAGING_SYSTEM, - MessagingIncubatingAttributes - .MessagingSystemIncubatingValues.AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs"), - equalTo(MESSAGING_OPERATION, "process"), - satisfies( - MESSAGING_MESSAGE_ID, - val -> val.isInstanceOf(String.class)), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1")), + span -> { + List attrs = new ArrayList<>(); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add( + equalTo( + AWS_SQS_QUEUE_URL, + "http://localhost:" + sqsPort + "/000000000000/testSdkSqs")); + attrs.add( + satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("AmazonSQS", "ReceiveMessage")); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add(equalTo(URL_FULL, "http://localhost:" + sqsPort)); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, sqsPort)); + attrs.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + span.hasName("SQS.ReceiveMessage") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(attrs); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add( + equalTo( + AWS_SQS_QUEUE_URL, + "http://localhost:" + sqsPort + "/000000000000/testSdkSqs")); + attrs.add( + satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("AmazonSQS", "ReceiveMessage")); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add(equalTo(URL_FULL, "http://localhost:" + sqsPort)); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, sqsPort)); + attrs.add( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues + .AWS_SQS)); + attrs.add(equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs")); + attrs.add(equalTo(MESSAGING_OPERATION, "receive")); + attrs.add( + equalTo( + MessagingIncubatingAttributes.MESSAGING_BATCH_MESSAGE_COUNT, + 1)); + attrs.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + span.hasName("testSdkSqs receive") + .hasKind(SpanKind.CONSUMER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(attrs); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add( + equalTo( + AWS_SQS_QUEUE_URL, + "http://localhost:" + sqsPort + "/000000000000/testSdkSqs")); + attrs.add( + satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("AmazonSQS", "ReceiveMessage")); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add(equalTo(URL_FULL, "http://localhost:" + sqsPort)); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, sqsPort)); + attrs.add( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues + .AWS_SQS)); + attrs.add(equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs")); + attrs.add(equalTo(MESSAGING_OPERATION, "process")); + attrs.add( + satisfies( + MESSAGING_MESSAGE_ID, val -> val.isInstanceOf(String.class))); + attrs.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + span.hasName("testSdkSqs process") + .hasKind(SpanKind.CONSUMER) + .hasParent(receiveSpan.get()) + .hasAttributesSatisfyingExactly(attrs); + }, span -> span.hasName("process child") .hasParent(processSpan.get()) diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/build.gradle.kts index b1d6d70f5747..6fd730cfa458 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-2.2/javaagent/build.gradle.kts @@ -203,9 +203,9 @@ tasks { excludeTestsMatching("Aws2SqsSuppressReceiveSpansTest") } systemProperty("otel.instrumentation.messaging.experimental.receive-telemetry.enabled", "true") - jvmArgs("-Dotel.semconv-stability.opt-in=database") + jvmArgs("-Dotel.semconv-stability.opt-in=rpc,database") - systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") + systemProperty("metadataConfig", "otel.semconv-stability.opt-in=rpc,database") } test { diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure/build.gradle.kts index 54daa03f7e08..6eba2f5cb2fa 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure/build.gradle.kts @@ -36,7 +36,7 @@ tasks { testClassesDirs = sourceSets.test.get().output.classesDirs classpath = sourceSets.test.get().runtimeClasspath - jvmArgs("-Dotel.semconv-stability.opt-in=database") + jvmArgs("-Dotel.semconv-stability.opt-in=rpc,database") } check { diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-2.2/library/build.gradle.kts index a44678d8d2a3..8925d2bce27c 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/build.gradle.kts @@ -81,7 +81,7 @@ tasks { testClassesDirs = sourceSets.test.get().output.classesDirs classpath = sourceSets.test.get().runtimeClasspath - jvmArgs("-Dotel.semconv-stability.opt-in=database") + jvmArgs("-Dotel.semconv-stability.opt-in=rpc,database") } check { diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java index f34767d4a771..cc820a3a5cbe 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientCoreTest.java @@ -9,6 +9,8 @@ import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStableDbSystemName; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_NAME; import static io.opentelemetry.semconv.DbAttributes.DB_SYSTEM_NAME; @@ -31,9 +33,6 @@ import static io.opentelemetry.semconv.incubating.AwsIncubatingAttributes.AWS_REQUEST_ID; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; @@ -257,44 +256,41 @@ private static CreateTableRequest createTableRequest() { @SuppressWarnings("deprecation") // uses deprecated semconv private static void assertListTablesRequest(SpanDataAssert span) { + List attrs = new ArrayList<>(); + attrs.add(equalTo(SERVER_ADDRESS, "127.0.0.1")); + attrs.add(equalTo(SERVER_PORT, server.httpPort())); + attrs.add(equalTo(URL_FULL, server.httpUri() + "/")); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("DynamoDb", "ListTables")); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add(equalTo(AWS_REQUEST_ID, "UNKNOWN")); + attrs.add(equalTo(AWS_DYNAMODB_TABLE_COUNT, 1)); + attrs.add(equalTo(maybeStable(DB_SYSTEM), maybeStableDbSystemName("dynamodb"))); + attrs.add(equalTo(maybeStable(DB_OPERATION), "ListTables")); span.hasName("DynamoDb.ListTables") .hasKind(SpanKind.CLIENT) .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(SERVER_ADDRESS, "127.0.0.1"), - equalTo(SERVER_PORT, server.httpPort()), - equalTo(URL_FULL, server.httpUri() + "/"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "DynamoDb"), - equalTo(RPC_METHOD, "ListTables"), - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_REQUEST_ID, "UNKNOWN"), - equalTo(AWS_DYNAMODB_TABLE_COUNT, 1), - equalTo(maybeStable(DB_SYSTEM), maybeStableDbSystemName("dynamodb")), - equalTo(maybeStable(DB_OPERATION), "ListTables")); + .hasAttributesSatisfyingExactly(attrs); } @SuppressWarnings("deprecation") // uses deprecated semconv private static void assertDynamoDbRequest( SpanDataAssert span, String operation, List extraAttributes) { - List assertions = - new ArrayList<>( - asList( - equalTo(SERVER_ADDRESS, "127.0.0.1"), - equalTo(SERVER_PORT, server.httpPort()), - equalTo(URL_FULL, server.httpUri() + "/"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "DynamoDb"), - equalTo(RPC_METHOD, operation), - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_REQUEST_ID, "UNKNOWN"), - equalTo(AWS_DYNAMODB_TABLE_NAMES, singletonList("sometable")), - equalTo(maybeStable(DB_SYSTEM), maybeStableDbSystemName("dynamodb")), - equalTo(maybeStable(DB_OPERATION), operation))); + List assertions = new ArrayList<>(); + assertions.add(equalTo(SERVER_ADDRESS, "127.0.0.1")); + assertions.add(equalTo(SERVER_PORT, server.httpPort())); + assertions.add(equalTo(URL_FULL, server.httpUri() + "/")); + assertions.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + assertions.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + assertions.add(rpcSystemAssertion("aws-api")); + assertions.addAll(rpcMethodAssertions("DynamoDb", operation)); + assertions.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + assertions.add(equalTo(AWS_REQUEST_ID, "UNKNOWN")); + assertions.add(equalTo(AWS_DYNAMODB_TABLE_NAMES, singletonList("sometable"))); + assertions.add(equalTo(maybeStable(DB_SYSTEM), maybeStableDbSystemName("dynamodb"))); + assertions.add(equalTo(maybeStable(DB_OPERATION), operation)); assertions.addAll(extraAttributes); span.hasName("DynamoDb." + operation) .hasKind(SpanKind.CLIENT) diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientRecordHttpErrorTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientRecordHttpErrorTest.java index 582af3e4a941..994c8b707154 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientRecordHttpErrorTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientRecordHttpErrorTest.java @@ -8,6 +8,8 @@ import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStableDbSystemName; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; @@ -18,14 +20,12 @@ import static io.opentelemetry.semconv.incubating.AwsIncubatingAttributes.AWS_REQUEST_ID; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; import static java.util.Collections.singletonList; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.testing.internal.armeria.common.HttpResponse; import io.opentelemetry.testing.internal.armeria.common.HttpStatus; import io.opentelemetry.testing.internal.armeria.common.MediaType; @@ -172,23 +172,26 @@ public void testSendDynamoDbRequestWithRetries() { trace -> trace.hasSpansSatisfyingExactly( span -> { + List attrs = new ArrayList<>(); + attrs.add(equalTo(SERVER_ADDRESS, "127.0.0.1")); + attrs.add(equalTo(SERVER_PORT, server.httpPort())); + attrs.add(equalTo(HTTP_REQUEST_METHOD, method)); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add( + equalTo( + stringKey("url.full"), + "http://127.0.0.1:" + server.httpPort() + "/")); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions(service, operation)); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add(equalTo(AWS_REQUEST_ID, requestId)); + attrs.add(equalTo(AWS_DYNAMODB_TABLE_NAMES, singletonList("sometable"))); + attrs.add( + equalTo(maybeStable(DB_SYSTEM), maybeStableDbSystemName("dynamodb"))); + attrs.add(equalTo(maybeStable(DB_OPERATION), operation)); span.hasKind(SpanKind.CLIENT); span.hasNoParent(); - span.hasAttributesSatisfyingExactly( - equalTo(SERVER_ADDRESS, "127.0.0.1"), - equalTo(SERVER_PORT, server.httpPort()), - equalTo(HTTP_REQUEST_METHOD, method), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo( - stringKey("url.full"), "http://127.0.0.1:" + server.httpPort() + "/"), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, service), - equalTo(RPC_METHOD, operation), - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_REQUEST_ID, requestId), - equalTo(AWS_DYNAMODB_TABLE_NAMES, singletonList("sometable")), - equalTo(maybeStable(DB_SYSTEM), maybeStableDbSystemName("dynamodb")), - equalTo(maybeStable(DB_OPERATION), operation)); + span.hasAttributesSatisfyingExactly(attrs); if (isRecordIndividualHttpErrorEnabled()) { span.hasEventsSatisfyingExactly( event -> diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java index b8d1892f062a..e30dab86ccd2 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2ClientTest.java @@ -6,6 +6,9 @@ package io.opentelemetry.instrumentation.awssdk.v2_2; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.errorTypeAssertion; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; @@ -27,9 +30,6 @@ import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MessagingSystemIncubatingValues.AWS_SQS; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -318,11 +318,10 @@ private void clientAssertions( equalTo(SERVER_PORT, server.httpPort()), equalTo(HTTP_REQUEST_METHOD, method), equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, service), - equalTo(RPC_METHOD, operation), + rpcSystemAssertion("aws-api"), equalTo(stringKey("aws.agent"), "java-aws-sdk"), equalTo(AWS_REQUEST_ID, requestId))); + attributes.addAll(rpcMethodAssertions(service, operation)); if (service.equals("S3")) { attributes.addAll( @@ -891,45 +890,51 @@ void testTimeoutAndRetryErrorsAreNotCaptured() { .waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( - span -> - span.hasName("S3.GetObject") - .hasKind(SpanKind.CLIENT) - .hasStatus(StatusData.error()) - .hasException(thrown) - .hasNoParent() - .hasAttributesSatisfyingExactly( - // Starting with AWS SDK V2 2.18.0, the s3 sdk will prefix the - // hostname with the bucket name in case the bucket name is a valid - // DNS label, even in the case that we are using an endpoint - // override. Previously the sdk was only doing that if endpoint had - // "s3" as label in the FQDN. Our test assert both cases so that we - // don't need to know what version is being tested. - satisfies( - SERVER_ADDRESS, - v -> v.matches("somebucket.localhost|localhost")), - satisfies( - URL_FULL, - val -> - val.satisfiesAnyOf( - v -> - assertThat(v) - .isEqualTo( - "http://somebucket.localhost:" - + server.httpPort() - + "/somekey"), - v -> - assertThat(v) - .isEqualTo( - "http://localhost:" - + server.httpPort() - + "/somebucket/somekey"))), - equalTo(SERVER_PORT, server.httpPort()), - equalTo(HTTP_REQUEST_METHOD, "GET"), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "S3"), - equalTo(RPC_METHOD, "GetObject"), - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_S3_BUCKET, "somebucket")))); + span -> { + List attrs = new ArrayList<>(); + // Starting with AWS SDK V2 2.18.0, the s3 sdk will prefix the + // hostname with the bucket name in case the bucket name is a valid + // DNS label, even in the case that we are using an endpoint + // override. Previously the sdk was only doing that if endpoint had + // "s3" as label in the FQDN. Our test assert both cases so that we + // don't need to know what version is being tested. + attrs.add( + satisfies( + SERVER_ADDRESS, v -> v.matches("somebucket.localhost|localhost"))); + attrs.add( + satisfies( + URL_FULL, + val -> + val.satisfiesAnyOf( + v -> + assertThat(v) + .isEqualTo( + "http://somebucket.localhost:" + + server.httpPort() + + "/somekey"), + v -> + assertThat(v) + .isEqualTo( + "http://localhost:" + + server.httpPort() + + "/somebucket/somekey")))); + attrs.add(equalTo(SERVER_PORT, server.httpPort())); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "GET")); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("S3", "GetObject")); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add(equalTo(AWS_S3_BUCKET, "somebucket")); + // error.type is added automatically in stable semconv + attrs.addAll( + errorTypeAssertion( + "software.amazon.awssdk.core.exception.SdkClientException")); + span.hasName("S3.GetObject") + .hasKind(SpanKind.CLIENT) + .hasStatus(StatusData.error()) + .hasException(thrown) + .hasNoParent() + .hasAttributesSatisfyingExactly(attrs); + })); } // regression test for diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsBaseTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsBaseTest.java index 3fb1636d551a..47ca9b3cbe8b 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsBaseTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsBaseTest.java @@ -6,6 +6,8 @@ package io.opentelemetry.instrumentation.awssdk.v2_2; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; @@ -20,19 +22,19 @@ import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MessagingSystemIncubatingValues.AWS_SQS; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -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; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; import io.opentelemetry.sdk.trace.data.SpanData; import java.net.URI; import java.net.URISyntaxException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.pekko.http.scaladsl.Http; import org.elasticmq.rest.sqs.SQSRestServer; @@ -203,74 +205,76 @@ void testSimpleSqsProducerConsumerServicesAsync() throws Exception { } static SpanDataAssert createQueueSpan(SpanDataAssert span) { + List attrs = new ArrayList<>(); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add(equalTo(stringKey("aws.queue.name"), "testSdkSqs")); + attrs.add( + satisfies( + AWS_REQUEST_ID, + val -> val.matches("\\s*00000000-0000-0000-0000-000000000000\\s*|UNKNOWN"))); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("Sqs", "CreateQueue")); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add(satisfies(URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort))); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, sqsPort)); return span.hasName("Sqs.CreateQueue") .hasKind(SpanKind.CLIENT) .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(stringKey("aws.queue.name"), "testSdkSqs"), - satisfies( - AWS_REQUEST_ID, - val -> val.matches("\\s*00000000-0000-0000-0000-000000000000\\s*|UNKNOWN")), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "Sqs"), - equalTo(RPC_METHOD, "CreateQueue"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - satisfies(URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort)), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort)); + .hasAttributesSatisfyingExactly(attrs); } @SuppressWarnings("deprecation") // using deprecated semconv static SpanDataAssert processSpan(SpanDataAssert span, SpanData parent) { + List attrs = new ArrayList<>(); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("Sqs", "ReceiveMessage")); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add(satisfies(URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort))); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, sqsPort)); + attrs.add(equalTo(MESSAGING_SYSTEM, AWS_SQS)); + attrs.add(equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs")); + attrs.add(equalTo(MESSAGING_OPERATION, "process")); + attrs.add(satisfies(MESSAGING_MESSAGE_ID, v -> v.isInstanceOf(String.class))); return span.hasName("testSdkSqs process") .hasKind(SpanKind.CONSUMER) .hasParent(parent) .hasTotalRecordedLinks(0) - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "Sqs"), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - satisfies(URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort)), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo(MESSAGING_SYSTEM, AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs"), - equalTo(MESSAGING_OPERATION, "process"), - satisfies(MESSAGING_MESSAGE_ID, v -> v.isInstanceOf(String.class))); + .hasAttributesSatisfyingExactly(attrs); } @SuppressWarnings("deprecation") // using deprecated semconv static SpanDataAssert publishSpan(SpanDataAssert span, String queueUrl, String rcpMethod) { + List attrs = new ArrayList<>(); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add(equalTo(AWS_SQS_QUEUE_URL, queueUrl)); + attrs.add( + satisfies( + AWS_REQUEST_ID, + val -> val.matches("\\s*00000000-0000-0000-0000-000000000000\\s*|UNKNOWN"))); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("Sqs", rcpMethod)); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add(satisfies(URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort))); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, sqsPort)); + attrs.add(equalTo(MESSAGING_SYSTEM, AWS_SQS)); + attrs.add(equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs")); + attrs.add(equalTo(MESSAGING_OPERATION, "publish")); + attrs.add( + satisfies( + MESSAGING_MESSAGE_ID, + val -> + val.satisfiesAnyOf( + v -> assertThat(v).isInstanceOf(String.class), v -> assertThat(v).isNull()))); return span.hasName("testSdkSqs publish") .hasKind(SpanKind.PRODUCER) .hasNoParent() - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_SQS_QUEUE_URL, queueUrl), - satisfies( - AWS_REQUEST_ID, - val -> val.matches("\\s*00000000-0000-0000-0000-000000000000\\s*|UNKNOWN")), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "Sqs"), - equalTo(RPC_METHOD, rcpMethod), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - satisfies(URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort)), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo(MESSAGING_SYSTEM, AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs"), - equalTo(MESSAGING_OPERATION, "publish"), - satisfies( - MESSAGING_MESSAGE_ID, - val -> - val.satisfiesAnyOf( - v -> assertThat(v).isInstanceOf(String.class), - v -> assertThat(v).isNull()))); + .hasAttributesSatisfyingExactly(attrs); } } diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsSuppressReceiveSpansTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsSuppressReceiveSpansTest.java index 8b15e7783284..4a821a7f591d 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsSuppressReceiveSpansTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsSuppressReceiveSpansTest.java @@ -6,6 +6,8 @@ package io.opentelemetry.instrumentation.awssdk.v2_2; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; @@ -20,14 +22,12 @@ import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MessagingSystemIncubatingValues.AWS_SQS; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; import io.opentelemetry.sdk.testing.assertj.TraceAssert; import java.net.URISyntaxException; @@ -68,26 +68,29 @@ protected void assertSqsTraces(boolean withParent, boolean captureHeaders) { trace -> trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasNoParent(), - span -> - span.hasName("Sqs.ReceiveMessage") - .hasKind(SpanKind.CLIENT) - .hasTotalRecordedLinks(0) - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(AWS_SQS_QUEUE_URL, queueUrl), - satisfies( - AWS_REQUEST_ID, - val -> - val.matches( - "\\s*00000000-0000-0000-0000-000000000000\\s*|UNKNOWN")), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "Sqs"), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - satisfies(URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort)), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort)))); + span -> { + List attrs = new ArrayList<>(); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add(equalTo(AWS_SQS_QUEUE_URL, queueUrl)); + attrs.add( + satisfies( + AWS_REQUEST_ID, + val -> + val.matches( + "\\s*00000000-0000-0000-0000-000000000000\\s*|UNKNOWN"))); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("Sqs", "ReceiveMessage")); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add( + satisfies(URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort))); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, sqsPort)); + span.hasName("Sqs.ReceiveMessage") + .hasKind(SpanKind.CLIENT) + .hasTotalRecordedLinks(0) + .hasAttributesSatisfyingExactly(attrs); + })); } getTesting().waitAndAssertTraces(traceAsserts); @@ -135,27 +138,29 @@ void testBatchSqsProducerConsumerServicesSync() throws URISyntaxException { traceAsserts.add( trace -> trace.hasSpansSatisfyingExactly( - span -> - span.hasName("testSdkSqs process") - .hasKind(SpanKind.CONSUMER) - // TODO: This is not good, and can also happen if producer is not - // instrumented - .hasNoParent() - .hasTotalRecordedLinks(0) - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "Sqs"), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - satisfies(URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort)), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo(MESSAGING_SYSTEM, AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs"), - equalTo(MESSAGING_OPERATION, "process"), - satisfies(MESSAGING_MESSAGE_ID, v -> v.isInstanceOf(String.class))))); + span -> { + List attrs = new ArrayList<>(); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("Sqs", "ReceiveMessage")); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add( + satisfies(URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort))); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, sqsPort)); + attrs.add(equalTo(MESSAGING_SYSTEM, AWS_SQS)); + attrs.add(equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs")); + attrs.add(equalTo(MESSAGING_OPERATION, "process")); + attrs.add(satisfies(MESSAGING_MESSAGE_ID, v -> v.isInstanceOf(String.class))); + span.hasName("testSdkSqs process") + .hasKind(SpanKind.CONSUMER) + // TODO: This is not good, and can also happen if producer is not + // instrumented + .hasNoParent() + .hasTotalRecordedLinks(0) + .hasAttributesSatisfyingExactly(attrs); + })); } getTesting().waitAndAssertTraces(traceAsserts); } diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsTracingTest.java b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsTracingTest.java index 98d676e288fc..42e70dff38cd 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsTracingTest.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/AbstractAws2SqsTracingTest.java @@ -7,6 +7,8 @@ import static io.opentelemetry.api.common.AttributeKey.stringArrayKey; import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; @@ -22,9 +24,6 @@ import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MessagingSystemIncubatingValues.AWS_SQS; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; @@ -64,32 +63,31 @@ protected void assertSqsTraces(boolean withParent, boolean captureHeaders) { trace.hasSpansSatisfyingExactly( span -> { publishSpan.set(trace.getSpan(0)); - List attributes = - new ArrayList<>( - Arrays.asList( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo( - AWS_SQS_QUEUE_URL, - "http://localhost:" + sqsPort + "/000000000000/testSdkSqs"), - satisfies( - AWS_REQUEST_ID, - val -> - val.matches( - "\\s*00000000-0000-0000-0000-000000000000\\s*|UNKNOWN")), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "Sqs"), - equalTo(RPC_METHOD, "SendMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - satisfies( - URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort)), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo(MESSAGING_SYSTEM, AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs"), - equalTo(MESSAGING_OPERATION, "publish"), - satisfies( - MESSAGING_MESSAGE_ID, v -> v.isInstanceOf(String.class)))); + List attributes = new ArrayList<>(); + attributes.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attributes.add( + equalTo( + AWS_SQS_QUEUE_URL, + "http://localhost:" + sqsPort + "/000000000000/testSdkSqs")); + attributes.add( + satisfies( + AWS_REQUEST_ID, + val -> + val.matches( + "\\s*00000000-0000-0000-0000-000000000000\\s*|UNKNOWN"))); + attributes.add(rpcSystemAssertion("aws-api")); + attributes.addAll(rpcMethodAssertions("Sqs", "SendMessage")); + attributes.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attributes.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attributes.add( + satisfies(URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort))); + attributes.add(equalTo(SERVER_ADDRESS, "localhost")); + attributes.add(equalTo(SERVER_PORT, sqsPort)); + attributes.add(equalTo(MESSAGING_SYSTEM, AWS_SQS)); + attributes.add(equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs")); + attributes.add(equalTo(MESSAGING_OPERATION, "publish")); + attributes.add( + satisfies(MESSAGING_MESSAGE_ID, v -> v.isInstanceOf(String.class))); if (captureHeaders) { attributes.add( @@ -114,52 +112,53 @@ protected void assertSqsTraces(boolean withParent, boolean captureHeaders) { * This one could be suppressed (by IF in TracingRequestHandler#beforeRequest but then * HTTP instrumentation span would appear) */ - span -> - span.hasName("Sqs.ReceiveMessage") - .hasKind(SpanKind.CLIENT) - .hasParent(trace.getSpan(0)) - .hasTotalRecordedLinks(0) - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo( - AWS_SQS_QUEUE_URL, - "http://localhost:" + sqsPort + "/000000000000/testSdkSqs"), - satisfies( - AWS_REQUEST_ID, - val -> - val.matches( - "\\s*00000000-0000-0000-0000-000000000000\\s*|UNKNOWN")), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "Sqs"), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - satisfies( - URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort)), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort)))); + span -> { + List attrs = new ArrayList<>(); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add( + equalTo( + AWS_SQS_QUEUE_URL, + "http://localhost:" + sqsPort + "/000000000000/testSdkSqs")); + attrs.add( + satisfies( + AWS_REQUEST_ID, + val -> + val.matches( + "\\s*00000000-0000-0000-0000-000000000000\\s*|UNKNOWN"))); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("Sqs", "ReceiveMessage")); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add( + satisfies( + URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort))); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, sqsPort)); + span.hasName("Sqs.ReceiveMessage") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasTotalRecordedLinks(0) + .hasAttributesSatisfyingExactly(attrs); + })); } spanAsserts.addAll( Arrays.asList( span -> { - List attributes = - new ArrayList<>( - Arrays.asList( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "Sqs"), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - satisfies( - URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort)), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo(MESSAGING_SYSTEM, AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs"), - equalTo(MESSAGING_OPERATION, "receive"), - equalTo(MESSAGING_BATCH_MESSAGE_COUNT, 1))); + List attributes = new ArrayList<>(); + attributes.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attributes.add(rpcSystemAssertion("aws-api")); + attributes.addAll(rpcMethodAssertions("Sqs", "ReceiveMessage")); + attributes.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attributes.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attributes.add( + satisfies(URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort))); + attributes.add(equalTo(SERVER_ADDRESS, "localhost")); + attributes.add(equalTo(SERVER_PORT, sqsPort)); + attributes.add(equalTo(MESSAGING_SYSTEM, AWS_SQS)); + attributes.add(equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs")); + attributes.add(equalTo(MESSAGING_OPERATION, "receive")); + attributes.add(equalTo(MESSAGING_BATCH_MESSAGE_COUNT, 1)); if (captureHeaders) { attributes.add( @@ -180,24 +179,21 @@ protected void assertSqsTraces(boolean withParent, boolean captureHeaders) { .hasAttributesSatisfyingExactly(attributes); }, span -> { - List attributes = - new ArrayList<>( - Arrays.asList( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "Sqs"), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - satisfies( - URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort)), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo(MESSAGING_SYSTEM, AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs"), - equalTo(MESSAGING_OPERATION, "process"), - satisfies( - MESSAGING_MESSAGE_ID, v -> v.isInstanceOf(String.class)))); + List attributes = new ArrayList<>(); + attributes.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attributes.add(rpcSystemAssertion("aws-api")); + attributes.addAll(rpcMethodAssertions("Sqs", "ReceiveMessage")); + attributes.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attributes.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attributes.add( + satisfies(URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort))); + attributes.add(equalTo(SERVER_ADDRESS, "localhost")); + attributes.add(equalTo(SERVER_PORT, sqsPort)); + attributes.add(equalTo(MESSAGING_SYSTEM, AWS_SQS)); + attributes.add(equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs")); + attributes.add(equalTo(MESSAGING_OPERATION, "process")); + attributes.add( + satisfies(MESSAGING_MESSAGE_ID, v -> v.isInstanceOf(String.class))); if (captureHeaders) { attributes.add( @@ -289,25 +285,27 @@ void testBatchSqsProducerConsumerServicesSync() throws URISyntaxException { trace -> { List> spanAsserts = new ArrayList<>(); spanAsserts.add( - span -> - span.hasName("testSdkSqs receive") - .hasKind(SpanKind.CONSUMER) - .hasNoParent() - .hasTotalRecordedLinks(0) - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "Sqs"), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - satisfies(URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort)), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo(MESSAGING_SYSTEM, AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs"), - equalTo(MESSAGING_OPERATION, "receive"), - equalTo(MESSAGING_BATCH_MESSAGE_COUNT, 3))); + span -> { + List attrs = new ArrayList<>(); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("Sqs", "ReceiveMessage")); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add( + satisfies(URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort))); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, sqsPort)); + attrs.add(equalTo(MESSAGING_SYSTEM, AWS_SQS)); + attrs.add(equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs")); + attrs.add(equalTo(MESSAGING_OPERATION, "receive")); + attrs.add(equalTo(MESSAGING_BATCH_MESSAGE_COUNT, 3)); + span.hasName("testSdkSqs receive") + .hasKind(SpanKind.CONSUMER) + .hasNoParent() + .hasTotalRecordedLinks(0) + .hasAttributesSatisfyingExactly(attrs); + }); // one of the 3 process spans is expected to not have a span link for (int i = 0; i <= 2; i++) { @@ -331,26 +329,27 @@ void testBatchSqsProducerConsumerServicesSync() throws URISyntaxException { .isEqualTo(publishSpan.get().getSpanId()))); } + List attrs = new ArrayList<>(); + attrs.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("Sqs", "ReceiveMessage")); + attrs.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add( + satisfies( + URL_FULL, v -> v.startsWith("http://localhost:" + sqsPort))); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, sqsPort)); + attrs.add(equalTo(MESSAGING_SYSTEM, AWS_SQS)); + attrs.add(equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs")); + attrs.add(equalTo(MESSAGING_OPERATION, "process")); + attrs.add( + satisfies( + MESSAGING_MESSAGE_ID, v -> v.isInstanceOf(String.class))); span.hasName("testSdkSqs process") .hasKind(SpanKind.CONSUMER) .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_SERVICE, "Sqs"), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - satisfies( - URL_FULL, - v -> v.startsWith("http://localhost:" + sqsPort)), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, sqsPort), - equalTo(MESSAGING_SYSTEM, AWS_SQS), - equalTo(MESSAGING_DESTINATION_NAME, "testSdkSqs"), - equalTo(MESSAGING_OPERATION, "process"), - satisfies( - MESSAGING_MESSAGE_ID, v -> v.isInstanceOf(String.class))); + .hasAttributesSatisfyingExactly(attrs); }, span -> span.hasName("process child") diff --git a/instrumentation/camel-2.20/javaagent/build.gradle.kts b/instrumentation/camel-2.20/javaagent/build.gradle.kts index b1166690fa58..4586bc8e4218 100644 --- a/instrumentation/camel-2.20/javaagent/build.gradle.kts +++ b/instrumentation/camel-2.20/javaagent/build.gradle.kts @@ -97,8 +97,8 @@ tasks { testClassesDirs = sourceSets.test.get().output.classesDirs classpath = sourceSets.test.get().runtimeClasspath - jvmArgs("-Dotel.semconv-stability.opt-in=database") - systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") + jvmArgs("-Dotel.semconv-stability.opt-in=database,rpc") + systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database,rpc") } check { diff --git a/instrumentation/camel-2.20/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachecamel/aws/AwsSpanAssertions.java b/instrumentation/camel-2.20/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachecamel/aws/AwsSpanAssertions.java index 00b924a719f3..1dc11db964fa 100644 --- a/instrumentation/camel-2.20/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachecamel/aws/AwsSpanAssertions.java +++ b/instrumentation/camel-2.20/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/apachecamel/aws/AwsSpanAssertions.java @@ -7,6 +7,8 @@ import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.api.trace.SpanKind.CLIENT; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; @@ -23,9 +25,6 @@ import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MessagingSystemIncubatingValues.AWS_SQS; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; @@ -93,9 +92,8 @@ static SpanDataAssert sqs( v -> assertThat(v).isNull(), v -> assertThat(v).isInstanceOf(Number.class))), equalTo(NETWORK_PROTOCOL_VERSION, "1.1"), - equalTo(RPC_SYSTEM, "aws-api"), - satisfies(RPC_METHOD, stringAssert -> stringAssert.isEqualTo(rpcMethod)), - equalTo(RPC_SERVICE, "AmazonSQS"))); + rpcSystemAssertion("aws-api"))); + attributeAssertions.addAll(rpcMethodAssertions("AmazonSQS", rpcMethod)); if (spanName.endsWith("receive") || spanName.endsWith("process") @@ -120,46 +118,49 @@ static SpanDataAssert sqs( } static SpanDataAssert s3(SpanDataAssert span, String spanName, String bucketName, String method) { - return span.hasName(spanName) - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - equalTo(stringKey("aws.bucket.name"), bucketName), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_METHOD, spanName.substring(3)), - equalTo(RPC_SERVICE, "Amazon S3"), - equalTo(HTTP_REQUEST_METHOD, method), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - satisfies(URL_FULL, val -> val.isInstanceOf(String.class)), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1"), - satisfies(SERVER_ADDRESS, val -> val.isInstanceOf(String.class)), - satisfies( - SERVER_PORT, - val -> - val.satisfiesAnyOf( - v -> val.isInstanceOf(Number.class), v -> assertThat(v).isNull()))); + List attributeAssertions = new ArrayList<>(); + attributeAssertions.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attributeAssertions.add(equalTo(stringKey("aws.bucket.name"), bucketName)); + attributeAssertions.add(rpcSystemAssertion("aws-api")); + attributeAssertions.addAll(rpcMethodAssertions("Amazon S3", spanName.substring(3))); + attributeAssertions.add(equalTo(HTTP_REQUEST_METHOD, method)); + attributeAssertions.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attributeAssertions.add(satisfies(URL_FULL, val -> val.isInstanceOf(String.class))); + attributeAssertions.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + attributeAssertions.add(satisfies(SERVER_ADDRESS, val -> val.isInstanceOf(String.class))); + attributeAssertions.add( + satisfies( + SERVER_PORT, + val -> + val.satisfiesAnyOf( + v -> val.isInstanceOf(Number.class), v -> assertThat(v).isNull()))); + + return span.hasName(spanName).hasAttributesSatisfyingExactly(attributeAssertions); } static SpanDataAssert sns( SpanDataAssert span, String spanName, String topicArn, String destinationName) { + List attributeAssertions = new ArrayList<>(); + attributeAssertions.add(equalTo(stringKey("aws.agent"), "java-aws-sdk")); + attributeAssertions.add(satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + attributeAssertions.add(equalTo(AWS_SNS_TOPIC_ARN, topicArn)); + attributeAssertions.add(rpcSystemAssertion("aws-api")); + attributeAssertions.addAll(rpcMethodAssertions("AmazonSNS", spanName.substring(4))); + attributeAssertions.add(equalTo(MESSAGING_DESTINATION_NAME, destinationName)); + attributeAssertions.add(equalTo(HTTP_REQUEST_METHOD, "POST")); + attributeAssertions.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attributeAssertions.add(satisfies(URL_FULL, val -> val.isInstanceOf(String.class))); + attributeAssertions.add(equalTo(NETWORK_PROTOCOL_VERSION, "1.1")); + attributeAssertions.add(satisfies(SERVER_ADDRESS, val -> val.isInstanceOf(String.class))); + attributeAssertions.add( + satisfies( + SERVER_PORT, + val -> + val.satisfiesAnyOf( + v -> val.isInstanceOf(Number.class), v -> assertThat(v).isNull()))); + return span.hasName(spanName) .hasKind(CLIENT) - .hasAttributesSatisfyingExactly( - equalTo(stringKey("aws.agent"), "java-aws-sdk"), - satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class)), - equalTo(AWS_SNS_TOPIC_ARN, topicArn), - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_METHOD, spanName.substring(4)), - equalTo(RPC_SERVICE, "AmazonSNS"), - equalTo(MESSAGING_DESTINATION_NAME, destinationName), - equalTo(HTTP_REQUEST_METHOD, "POST"), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - satisfies(URL_FULL, val -> val.isInstanceOf(String.class)), - equalTo(NETWORK_PROTOCOL_VERSION, "1.1"), - satisfies(SERVER_ADDRESS, val -> val.isInstanceOf(String.class)), - satisfies( - SERVER_PORT, - val -> - val.satisfiesAnyOf( - v -> val.isInstanceOf(Number.class), v -> assertThat(v).isNull()))); + .hasAttributesSatisfyingExactly(attributeAssertions); } } diff --git a/instrumentation/grpc-1.6/javaagent/build.gradle.kts b/instrumentation/grpc-1.6/javaagent/build.gradle.kts index d4eca5080f1d..b1bc32053a05 100644 --- a/instrumentation/grpc-1.6/javaagent/build.gradle.kts +++ b/instrumentation/grpc-1.6/javaagent/build.gradle.kts @@ -30,52 +30,51 @@ dependencies { val collectMetadata = findProperty("collectMetadata")?.toString() ?: "false" +fun Test.configureTestTask() { + 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. + 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") + // 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") + + // 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) +} + tasks { test { - 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. - 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") - // 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") - - // 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) + configureTestTask() } val testExperimental by registering(Test::class) { 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) + configureTestTask() 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 + + configureTestTask() + jvmArgs("-Dotel.semconv-stability.opt-in=rpc") + } + check { - dependsOn(testExperimental) + dependsOn(testExperimental, testStableSemconv) } } diff --git a/instrumentation/grpc-1.6/library/build.gradle.kts b/instrumentation/grpc-1.6/library/build.gradle.kts index 00ace4566efb..9463449ac9e8 100644 --- a/instrumentation/grpc-1.6/library/build.gradle.kts +++ b/instrumentation/grpc-1.6/library/build.gradle.kts @@ -16,13 +16,29 @@ dependencies { testImplementation(project(":instrumentation:grpc-1.6:testing")) } +fun Test.configureTestTask() { + 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") +} + tasks { test { - 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") + configureTestTask() + } + + val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + configureTestTask() + jvmArgs("-Dotel.semconv-stability.opt-in=rpc") + } + + check { + dependsOn(testStableSemconv) } } 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 aa46a91f8ddb..0446a8e27ec1 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( @@ -34,5 +38,15 @@ private static AttributeKey> createRequestKey(String metadataKey) { return AttributeKey.stringArrayKey(RPC_REQUEST_METADATA_KEY_ATTRIBUTE_PREFIX + metadataKey); } + static AttributeKey> stableRequestAttributeKey(String metadataKey) { + return stableRequestKeysCache.computeIfAbsent( + metadataKey, CapturedGrpcMetadataUtil::createStableRequestKey); + } + + 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..989d148929a6 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 @@ -7,18 +7,25 @@ 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; import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; import java.util.List; import javax.annotation.Nullable; final class GrpcAttributesExtractor implements AttributesExtractor { + // Stable semconv key + private static final AttributeKey RPC_RESPONSE_STATUS_CODE = + AttributeKey.stringKey("rpc.response.status_code"); + // copied from RpcIncubatingAttributes + @Deprecated // use RPC_RESPONSE_STATUS_CODE for stable semconv private static final AttributeKey RPC_GRPC_STATUS_CODE = AttributeKey.longKey("rpc.grpc.status_code"); @@ -36,6 +43,7 @@ public void onStart(AttributesBuilder attributes, Context parentContext, GrpcReq // Request attributes captured on request end. } + @SuppressWarnings("deprecation") // until old rpc semconv are dropped @Override public void onEnd( AttributesBuilder attributes, @@ -44,12 +52,23 @@ public void onEnd( @Nullable Status status, @Nullable Throwable error) { if (status != null) { - attributes.put(RPC_GRPC_STATUS_CODE, status.getCode().value()); + long statusCodeValue = status.getCode().value(); + if (SemconvStability.emitStableRpcSemconv()) { + attributes.put(RPC_RESPONSE_STATUS_CODE, String.valueOf(statusCodeValue)); + } + if (SemconvStability.emitOldRpcSemconv()) { + attributes.put(RPC_GRPC_STATUS_CODE, statusCodeValue); + } } for (String key : capturedRequestMetadata) { List value = getter.metadataValue(request, key); if (!value.isEmpty()) { - attributes.put(requestAttributeKey(key), value); + if (SemconvStability.emitStableRpcSemconv()) { + attributes.put(stableRequestAttributeKey(key), value); + } + if (SemconvStability.emitOldRpcSemconv()) { + attributes.put(requestAttributeKey(key), value); + } } } } 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 e38b95c40f85..27482f3fe96f 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 @@ -55,6 +55,13 @@ public Long getResponseSize(GrpcRequest request) { return request.getResponseSize(); } + @Override + @Nullable + public String getFullMethod(GrpcRequest request) { + // gRPC already has fully-qualified method name, avoid reconstructing it + return request.getMethod().getFullMethodName(); + } + List metadataValue(GrpcRequest request, String key) { if (request.getMetadata() == null) { return Collections.emptyList(); 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 fa46f3dbb26c..abe762517a74 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 @@ -9,6 +9,12 @@ 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.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.getClientDurationMetricName; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.getDurationUnit; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.getServerDurationMetricName; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.grpcStatusCodeAssertion; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; 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; @@ -17,10 +23,6 @@ import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_TYPE; import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; -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_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; import example.GreeterGrpc; import example.Helloworld; @@ -36,6 +38,7 @@ import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.util.ThrowingRunnable; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.sdk.trace.data.EventData; import io.opentelemetry.semconv.incubating.MessageIncubatingAttributes; import java.util.ArrayList; @@ -220,99 +223,106 @@ public void onCompleted() { .waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( - span -> - span.hasName("example.Greeter/Conversation") - .hasKind(SpanKind.CLIENT) - .hasNoParent() - .hasAttributesSatisfyingExactly( - addExtraClientAttributes( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - 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(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, (long) server.getPort()))) - .satisfies( - spanData -> - assertThat(spanData.getEvents()) - .satisfiesExactlyInAnyOrder(toArray(clientEvents))), - span -> - span.hasName("example.Greeter/Conversation") - .hasKind(SpanKind.SERVER) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - 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(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, server.getPort()), - equalTo(NETWORK_TYPE, "ipv4"), - equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - v -> assertThat(v).isGreaterThan(0)), - experimentalSatisfies( - GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0)), - satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) - .satisfies( - spanData -> - assertThat(spanData.getEvents()) - .satisfiesExactlyInAnyOrder(toArray(serverEvents))))); + span -> { + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions("example.Greeter", "Conversation")); + attrs.add(grpcStatusCodeAssertion(Status.Code.OK.value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, (long) server.getPort())); + span.hasName("example.Greeter/Conversation") + .hasKind(SpanKind.CLIENT) + .hasNoParent() + .hasAttributesSatisfyingExactly(addExtraClientAttributes(attrs)) + .satisfies( + spanData -> + assertThat(spanData.getEvents()) + .satisfiesExactlyInAnyOrder(toArray(clientEvents))); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions("example.Greeter", "Conversation")); + attrs.add(grpcStatusCodeAssertion(Status.Code.OK.value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, server.getPort())); + attrs.add(equalTo(NETWORK_TYPE, "ipv4")); + attrs.add(equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1")); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())); + span.hasName("example.Greeter/Conversation") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(attrs) + .satisfies( + spanData -> + assertThat(spanData.getEvents()) + .satisfiesExactlyInAnyOrder(toArray(serverEvents))); + })); testing() .waitAndAssertMetrics( "io.opentelemetry.grpc-1.6", - "rpc.server.duration", + getServerDurationMetricName(), metrics -> metrics.anySatisfy( metric -> assertThat(metric) - .hasUnit("ms") + .hasUnit(getDurationUnit()) .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())))))); + point -> { + List attrs = new ArrayList<>(); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll( + rpcMethodAssertions( + "example.Greeter", "Conversation")); + attrs.add( + grpcStatusCodeAssertion(Status.Code.OK.value())); + point.hasAttributesSatisfying(attrs); + })))); testing() .waitAndAssertMetrics( "io.opentelemetry.grpc-1.6", - "rpc.client.duration", + getClientDurationMetricName(), metrics -> metrics.anySatisfy( metric -> assertThat(metric) - .hasUnit("ms") + .hasUnit(getDurationUnit()) .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())))))); + point -> { + List attrs = new ArrayList<>(); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, server.getPort())); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll( + rpcMethodAssertions( + "example.Greeter", "Conversation")); + attrs.add( + grpcStatusCodeAssertion(Status.Code.OK.value())); + point.hasAttributesSatisfying(attrs); + })))); } @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 bd85ef907f43..bd938ea83e65 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 @@ -8,6 +8,13 @@ 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.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.errorTypeAssertion; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.getClientDurationMetricName; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.getDurationUnit; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.getServerDurationMetricName; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.grpcStatusCodeAssertion; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; 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; @@ -16,10 +23,6 @@ import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_TYPE; import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; -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_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.params.provider.Arguments.arguments; @@ -55,13 +58,13 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.util.ThrowingRunnable; import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.semconv.incubating.MessageIncubatingAttributes; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Queue; @@ -138,75 +141,73 @@ 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)) - .hasAttributesSatisfyingExactly( - addExtraClientAttributes( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - 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(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, (long) server.getPort()))) - .hasEventsSatisfyingExactly( - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, - "RECEIVED"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))), - span -> - span.hasName("example.Greeter/SayHello") - .hasKind(SpanKind.SERVER) - .hasParent(trace.getSpan(1)) - .hasAttributesSatisfyingExactly( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - 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(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, server.getPort()), - equalTo(NETWORK_TYPE, "ipv4"), - equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) - .hasEventsSatisfyingExactly( - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, - "RECEIVED"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))))); + span -> { + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions("example.Greeter", "SayHello")); + attrs.add(grpcStatusCodeAssertion(Status.Code.OK.value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, (long) server.getPort())); + span.hasName("example.Greeter/SayHello") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(addExtraClientAttributes(attrs)) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo(MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo( + MessageIncubatingAttributes.MESSAGE_TYPE, "RECEIVED"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions("example.Greeter", "SayHello")); + attrs.add(grpcStatusCodeAssertion(Status.Code.OK.value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, server.getPort())); + attrs.add(equalTo(NETWORK_TYPE, "ipv4")); + attrs.add(equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1")); + attrs.add(satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())); + span.hasName("example.Greeter/SayHello") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly(attrs) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo( + MessageIncubatingAttributes.MESSAGE_TYPE, "RECEIVED"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo(MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))); + })); assertMetrics(server, Status.Code.OK); } @@ -263,75 +264,73 @@ 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)) - .hasAttributesSatisfyingExactly( - addExtraClientAttributes( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - 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(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, (long) server.getPort()))) - .hasEventsSatisfyingExactly( - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, - "RECEIVED"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))), - span -> - span.hasName("example.Greeter/SayHello") - .hasKind(SpanKind.SERVER) - .hasParent(trace.getSpan(1)) - .hasAttributesSatisfyingExactly( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - 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(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, server.getPort()), - equalTo(NETWORK_TYPE, "ipv4"), - equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) - .hasEventsSatisfyingExactly( - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, - "RECEIVED"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))), + span -> { + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions("example.Greeter", "SayHello")); + attrs.add(grpcStatusCodeAssertion(Status.Code.OK.value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, (long) server.getPort())); + span.hasName("example.Greeter/SayHello") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(addExtraClientAttributes(attrs)) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo(MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo( + MessageIncubatingAttributes.MESSAGE_TYPE, "RECEIVED"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions("example.Greeter", "SayHello")); + attrs.add(grpcStatusCodeAssertion(Status.Code.OK.value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, server.getPort())); + attrs.add(equalTo(NETWORK_TYPE, "ipv4")); + attrs.add(equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1")); + attrs.add(satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())); + span.hasName("example.Greeter/SayHello") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly(attrs) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo( + MessageIncubatingAttributes.MESSAGE_TYPE, "RECEIVED"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo(MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))); + }, span -> span.hasName("child") .hasKind(SpanKind.INTERNAL) @@ -400,75 +399,73 @@ public void onCompleted() { trace -> trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), - span -> - span.hasName("example.Greeter/SayHello") - .hasKind(SpanKind.CLIENT) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - addExtraClientAttributes( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - 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(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, (long) server.getPort()))) - .hasEventsSatisfyingExactly( - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, - "RECEIVED"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))), - span -> - span.hasName("example.Greeter/SayHello") - .hasKind(SpanKind.SERVER) - .hasParent(trace.getSpan(1)) - .hasAttributesSatisfyingExactly( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - 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(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, server.getPort()), - equalTo(NETWORK_TYPE, "ipv4"), - equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) - .hasEventsSatisfyingExactly( - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, - "RECEIVED"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))), + span -> { + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions("example.Greeter", "SayHello")); + attrs.add(grpcStatusCodeAssertion(Status.Code.OK.value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, (long) server.getPort())); + span.hasName("example.Greeter/SayHello") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(addExtraClientAttributes(attrs)) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo(MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo( + MessageIncubatingAttributes.MESSAGE_TYPE, "RECEIVED"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions("example.Greeter", "SayHello")); + attrs.add(grpcStatusCodeAssertion(Status.Code.OK.value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, server.getPort())); + attrs.add(equalTo(NETWORK_TYPE, "ipv4")); + attrs.add(equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1")); + attrs.add(satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())); + span.hasName("example.Greeter/SayHello") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly(attrs) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo( + MessageIncubatingAttributes.MESSAGE_TYPE, "RECEIVED"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo(MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))); + }, span -> span.hasName("child") .hasKind(SpanKind.INTERNAL) @@ -509,69 +506,75 @@ public void sayHello( .waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( - span -> - span.hasName("example.Greeter/SayHello") - .hasKind(SpanKind.CLIENT) - .hasNoParent() - .hasStatus(StatusData.error()) - .hasAttributesSatisfyingExactly( - addExtraClientAttributes( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - v -> assertThat(v).isEqualTo(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.getCode().value()), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, (long) server.getPort()))) - .hasEventsSatisfyingExactly( - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))), - span -> - span.hasName("example.Greeter/SayHello") - .hasKind(SpanKind.SERVER) - .hasParent(trace.getSpan(0)) - .hasStatus(isServerError ? StatusData.error() : StatusData.unset()) - .hasAttributesSatisfyingExactly( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - 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(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, server.getPort()), - equalTo(NETWORK_TYPE, "ipv4"), - equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) - .hasEventsSatisfying( - events -> { - assertThat(events).isNotEmpty(); - assertThat(events.get(0)) + span -> { + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isEqualTo(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions("example.Greeter", "SayHello")); + attrs.add(grpcStatusCodeAssertion(status.getCode().value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, (long) server.getPort())); + span.hasName("example.Greeter/SayHello") + .hasKind(SpanKind.CLIENT) + .hasNoParent() + .hasStatus(StatusData.error()) + .hasAttributesSatisfyingExactly(addExtraClientAttributes(attrs)) + .hasEventsSatisfyingExactly( + event -> + event .hasName("message") .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, "RECEIVED"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)); - if (status.getCause() == null) { - assertThat(events).hasSize(1); - } else { - assertThat(events).hasSize(2); - span.hasException(status.getCause()); - } - }))); + equalTo(MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isEqualTo(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions("example.Greeter", "SayHello")); + attrs.add(grpcStatusCodeAssertion(status.getCode().value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, server.getPort())); + attrs.add(equalTo(NETWORK_TYPE, "ipv4")); + attrs.add(equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1")); + attrs.add(satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())); + // error.type is added automatically in stable semconv when there's an + // exception + if (status.getCause() != null) { + attrs.addAll(errorTypeAssertion(status.getCause().getClass().getName())); + } + span.hasName("example.Greeter/SayHello") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(0)) + .hasStatus(isServerError ? StatusData.error() : StatusData.unset()) + .hasAttributesSatisfyingExactly(attrs) + .hasEventsSatisfying( + events -> { + assertThat(events).isNotEmpty(); + assertThat(events.get(0)) + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo( + MessageIncubatingAttributes.MESSAGE_TYPE, "RECEIVED"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)); + if (status.getCause() == null) { + assertThat(events).hasSize(1); + } else { + assertThat(events).hasSize(2); + span.hasException(status.getCause()); + } + }); + })); assertMetrics(server, status.getCode()); } @@ -610,66 +613,66 @@ public void sayHello( .waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( - span -> - // NB: Exceptions thrown on the server don't appear to be propagated to the - // client, at - // least for the version we test against, so the client gets an UNKNOWN - // status and the server - // doesn't record one at all. - span.hasName("example.Greeter/SayHello") - .hasKind(SpanKind.CLIENT) - .hasNoParent() - .hasStatus(StatusData.error()) - .hasAttributesSatisfyingExactly( - addExtraClientAttributes( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - v -> assertThat(v).isEqualTo(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.UNKNOWN.getCode().value()), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, (long) server.getPort()))) - .hasEventsSatisfyingExactly( - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))), - span -> - span.hasName("example.Greeter/SayHello") - .hasKind(SpanKind.SERVER) - .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(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, server.getPort()), - equalTo(NETWORK_TYPE, "ipv4"), - equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) - .hasEventsSatisfying( - events -> { - assertThat(events).hasSize(2); - assertThat(events.get(0)) + span -> { + // NB: Exceptions thrown on the server don't appear to be propagated to the + // client, at + // least for the version we test against, so the client gets an UNKNOWN + // status and the server + // doesn't record one at all. + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isEqualTo(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions("example.Greeter", "SayHello")); + attrs.add(grpcStatusCodeAssertion(Status.UNKNOWN.getCode().value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, (long) server.getPort())); + span.hasName("example.Greeter/SayHello") + .hasKind(SpanKind.CLIENT) + .hasNoParent() + .hasStatus(StatusData.error()) + .hasAttributesSatisfyingExactly(addExtraClientAttributes(attrs)) + .hasEventsSatisfyingExactly( + event -> + event .hasName("message") .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, "RECEIVED"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)); - span.hasException(status.asRuntimeException()); - }))); + equalTo(MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions("example.Greeter", "SayHello")); + attrs.add(grpcStatusCodeAssertion(Status.Code.UNKNOWN.value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, server.getPort())); + attrs.add(equalTo(NETWORK_TYPE, "ipv4")); + attrs.add(equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1")); + attrs.add(satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())); + // error.type is added automatically in stable semconv + attrs.addAll(errorTypeAssertion("io.grpc.StatusRuntimeException")); + span.hasName("example.Greeter/SayHello") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(0)) + .hasStatus(StatusData.error()) + .hasAttributesSatisfyingExactly(attrs) + .hasEventsSatisfying( + events -> { + assertThat(events).hasSize(2); + assertThat(events.get(0)) + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo( + MessageIncubatingAttributes.MESSAGE_TYPE, "RECEIVED"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)); + span.hasException(status.asRuntimeException()); + }); + })); assertMetrics(server, Status.Code.UNKNOWN); } @@ -815,75 +818,73 @@ public void onCompleted() { trace -> trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), - span -> - span.hasName("example.Greeter/SayHello") - .hasKind(SpanKind.CLIENT) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - addExtraClientAttributes( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - 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(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, (long) server.getPort()))) - .hasEventsSatisfyingExactly( - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, - "RECEIVED"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))), - span -> - span.hasName("example.Greeter/SayHello") - .hasKind(SpanKind.SERVER) - .hasParent(trace.getSpan(1)) - .hasAttributesSatisfyingExactly( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - 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(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, server.getPort()), - equalTo(NETWORK_TYPE, "ipv4"), - equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) - .hasEventsSatisfyingExactly( - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, - "RECEIVED"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))))); + span -> { + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions("example.Greeter", "SayHello")); + attrs.add(grpcStatusCodeAssertion(Status.Code.OK.value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, (long) server.getPort())); + span.hasName("example.Greeter/SayHello") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(addExtraClientAttributes(attrs)) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo(MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo( + MessageIncubatingAttributes.MESSAGE_TYPE, "RECEIVED"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions("example.Greeter", "SayHello")); + attrs.add(grpcStatusCodeAssertion(Status.Code.OK.value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, server.getPort())); + attrs.add(equalTo(NETWORK_TYPE, "ipv4")); + attrs.add(equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1")); + attrs.add(satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())); + span.hasName("example.Greeter/SayHello") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly(attrs) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo( + MessageIncubatingAttributes.MESSAGE_TYPE, "RECEIVED"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo(MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))); + })); } @Test @@ -945,77 +946,78 @@ public void onCompleted() { trace -> trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), - span -> - span.hasName("example.Greeter/SayMultipleHello") - .hasKind(SpanKind.CLIENT) - .hasParent(trace.getSpan(0)) - .hasStatus(StatusData.error()) - .hasAttributesSatisfyingExactly( - addExtraClientAttributes( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - 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(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, (long) server.getPort()))) - .hasEventsSatisfying( - events -> { - assertThat(events).hasSize(3); - assertThat(events.get(0)) - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo(MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)); - assertThat(events.get(1)) + span -> { + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions("example.Greeter", "SayMultipleHello")); + attrs.add(grpcStatusCodeAssertion(Status.Code.CANCELLED.value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, (long) server.getPort())); + // error.type is added automatically in stable semconv when client throws + attrs.addAll(errorTypeAssertion("java.lang.IllegalStateException")); + span.hasName("example.Greeter/SayMultipleHello") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasStatus(StatusData.error()) + .hasAttributesSatisfyingExactly(addExtraClientAttributes(attrs)) + .hasEventsSatisfying( + events -> { + assertThat(events).hasSize(3); + assertThat(events.get(0)) + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo(MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)); + assertThat(events.get(1)) + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo( + MessageIncubatingAttributes.MESSAGE_TYPE, "RECEIVED"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)); + span.hasException(thrown); + }); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions("example.Greeter", "SayMultipleHello")); + attrs.add(grpcStatusCodeAssertion(Status.Code.CANCELLED.value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, server.getPort())); + attrs.add(equalTo(NETWORK_TYPE, "ipv4")); + attrs.add(equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1")); + attrs.add(satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())); + span.hasName("example.Greeter/SayMultipleHello") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly(attrs) + .hasEventsSatisfyingExactly( + event -> + event .hasName("message") .hasAttributesSatisfyingExactly( equalTo( MessageIncubatingAttributes.MESSAGE_TYPE, "RECEIVED"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)); - span.hasException(thrown); - }), - span -> - span.hasName("example.Greeter/SayMultipleHello") - .hasKind(SpanKind.SERVER) - .hasParent(trace.getSpan(1)) - .hasAttributesSatisfyingExactly( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - 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(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, server.getPort()), - equalTo(NETWORK_TYPE, "ipv4"), - equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) - .hasEventsSatisfyingExactly( - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, - "RECEIVED"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))))); + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo(MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))); + })); } @Test @@ -1072,78 +1074,77 @@ public void onCompleted() { .waitAndAssertTraces( trace -> trace.hasSpansSatisfyingExactly( - span -> - span.hasName( - "grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo") - .hasKind(SpanKind.CLIENT) - .hasNoParent() - .hasAttributesSatisfyingExactly( - addExtraClientAttributes( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - 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(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, (long) server.getPort()))) - .hasEventsSatisfyingExactly( - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, - "RECEIVED"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))), - span -> - span.hasName( - "grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo") - .hasKind(SpanKind.SERVER) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - 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(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, server.getPort()), - equalTo(NETWORK_TYPE, "ipv4"), - equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) - .hasEventsSatisfyingExactly( - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, - "RECEIVED"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))))); + span -> { + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll( + rpcMethodAssertions( + "grpc.reflection.v1alpha.ServerReflection", "ServerReflectionInfo")); + attrs.add(grpcStatusCodeAssertion(Status.Code.OK.value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, (long) server.getPort())); + span.hasName("grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo") + .hasKind(SpanKind.CLIENT) + .hasNoParent() + .hasAttributesSatisfyingExactly(addExtraClientAttributes(attrs)) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo(MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo( + MessageIncubatingAttributes.MESSAGE_TYPE, "RECEIVED"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll( + rpcMethodAssertions( + "grpc.reflection.v1alpha.ServerReflection", "ServerReflectionInfo")); + attrs.add(grpcStatusCodeAssertion(Status.Code.OK.value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, server.getPort())); + attrs.add(equalTo(NETWORK_TYPE, "ipv4")); + attrs.add(equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1")); + attrs.add(satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())); + span.hasName("grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(attrs) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo( + MessageIncubatingAttributes.MESSAGE_TYPE, "RECEIVED"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo(MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))); + })); } @Test @@ -1184,75 +1185,73 @@ 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)) - .hasAttributesSatisfyingExactly( - addExtraClientAttributes( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - 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(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, (long) server.getPort()))) - .hasEventsSatisfyingExactly( - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, - "RECEIVED"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))), - span -> - span.hasName("example.Greeter/SayHello") - .hasKind(SpanKind.SERVER) - .hasParent(trace.getSpan(1)) - .hasAttributesSatisfyingExactly( - experimentalSatisfies( - GRPC_RECEIVED_MESSAGE_COUNT, - 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(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, server.getPort()), - equalTo(NETWORK_TYPE, "ipv4"), - equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), - satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())) - .hasEventsSatisfyingExactly( - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, - "RECEIVED"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), - event -> - event - .hasName("message") - .hasAttributesSatisfyingExactly( - equalTo( - MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), - equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))))); + span -> { + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions("example.Greeter", "SayHello")); + attrs.add(grpcStatusCodeAssertion(Status.Code.OK.value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, (long) server.getPort())); + span.hasName("example.Greeter/SayHello") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(addExtraClientAttributes(attrs)) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo(MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo( + MessageIncubatingAttributes.MESSAGE_TYPE, "RECEIVED"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add( + experimentalSatisfies( + GRPC_RECEIVED_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add( + experimentalSatisfies( + GRPC_SENT_MESSAGE_COUNT, v -> assertThat(v).isGreaterThan(0))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions("example.Greeter", "SayHello")); + attrs.add(grpcStatusCodeAssertion(Status.Code.OK.value())); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, server.getPort())); + attrs.add(equalTo(NETWORK_TYPE, "ipv4")); + attrs.add(equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1")); + attrs.add(satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())); + span.hasName("example.Greeter/SayHello") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfyingExactly(attrs) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo( + MessageIncubatingAttributes.MESSAGE_TYPE, "RECEIVED"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)), + event -> + event + .hasName("message") + .hasAttributesSatisfyingExactly( + equalTo(MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), + equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))); + })); } // Regression test for @@ -1402,7 +1401,10 @@ public void sayHello( @Test void setCapturedRequestMetadata() throws Exception { - String metadataAttributePrefix = "rpc.grpc.request.metadata."; + String metadataAttributePrefix = + SemconvStability.emitStableRpcSemconv() + ? "rpc.request.metadata." + : "rpc.grpc.request.metadata."; AttributeKey> clientAttributeKey = AttributeKey.stringArrayKey(metadataAttributePrefix + CLIENT_REQUEST_METADATA_KEY); AttributeKey> serverAttributeKey = @@ -1489,15 +1491,38 @@ private static void usePlainText(ManagedChannelBuilder channelBuilder) throws } } - static List addExtraClientAttributes(AttributeAssertion... assertions) { - List result = new ArrayList<>(); - result.addAll(Arrays.asList(assertions)); + private static List buildServerMetricAttributes( + String service, String method, long statusCode) { + List attrs = new ArrayList<>(); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(satisfies(SERVER_PORT, k -> k.isInstanceOf(Long.class))); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions(service, method)); + attrs.add(grpcStatusCodeAssertion(statusCode)); + if (SemconvStability.emitOldRpcSemconv()) { + attrs.add(equalTo(NETWORK_TYPE, "ipv4")); + } + return attrs; + } + + private static List buildClientMetricAttributes( + String service, String method, long statusCode, long serverPort) { + List attrs = new ArrayList<>(); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, serverPort)); + attrs.add(rpcSystemAssertion("grpc")); + attrs.addAll(rpcMethodAssertions(service, method)); + attrs.add(grpcStatusCodeAssertion(statusCode)); + return attrs; + } + + static List addExtraClientAttributes(List assertions) { if (Boolean.getBoolean("testLatestDeps")) { - result.add(equalTo(NETWORK_TYPE, "ipv4")); - result.add(equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1")); - result.add(satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())); + assertions.add(equalTo(NETWORK_TYPE, "ipv4")); + assertions.add(equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1")); + assertions.add(satisfies(NETWORK_PEER_PORT, val -> val.isNotNull())); } - return result; + return assertions; } private void assertMetrics(Server server, Status.Code statusCode) { @@ -1505,29 +1530,24 @@ private void assertMetrics(Server server, Status.Code statusCode) { testing() .waitAndAssertMetrics( "io.opentelemetry.grpc-1.6", - "rpc.server.duration", + getServerDurationMetricName(), metrics -> metrics.anySatisfy( metric -> assertThat(metric) - .hasUnit("ms") + .hasUnit(getDurationUnit()) .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) { + buildServerMetricAttributes( + "example.Greeter", + "SayHello", + (long) statusCode.value())))))); + + // Size metrics are only in old semconv + if (hasSizeMetric && SemconvStability.emitOldRpcSemconv()) { testing() .waitAndAssertMetrics( "io.opentelemetry.grpc-1.6", @@ -1542,16 +1562,10 @@ private void assertMetrics(Server server, Status.Code statusCode) { 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")))))); + buildServerMetricAttributes( + "example.Greeter", + "SayHello", + (long) statusCode.value())))))); testing() .waitAndAssertMetrics( "io.opentelemetry.grpc-1.6", @@ -1566,64 +1580,55 @@ private void assertMetrics(Server server, Status.Code statusCode) { 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")))))); + buildServerMetricAttributes( + "example.Greeter", + "SayHello", + (long) statusCode.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, "SayHello"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_SYSTEM, "grpc"), - equalTo( - RPC_GRPC_STATUS_CODE, - (long) statusCode.value())))))); - - testing() - .waitAndAssertMetrics( - "io.opentelemetry.grpc-1.6", - "rpc.client.request.size", + getClientDurationMetricName(), metrics -> metrics.anySatisfy( metric -> assertThat(metric) - .hasUnit("By") + .hasUnit(getDurationUnit()) .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) { + buildClientMetricAttributes( + "example.Greeter", + "SayHello", + (long) statusCode.value(), + server.getPort())))))); + + // Size metrics are only in old semconv + if (hasSizeMetric && SemconvStability.emitOldRpcSemconv()) { + 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( + buildClientMetricAttributes( + "example.Greeter", + "SayHello", + (long) statusCode.value(), + server.getPort())))))); + } + if (hasSizeMetric && SemconvStability.emitOldRpcSemconv()) { testing() .waitAndAssertMetrics( "io.opentelemetry.grpc-1.6", @@ -1638,14 +1643,11 @@ private void assertMetrics(Server server, Status.Code statusCode) { 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())))))); + buildClientMetricAttributes( + "example.Greeter", + "SayHello", + (long) statusCode.value(), + server.getPort())))))); } } } diff --git a/instrumentation/gwt-2.0/javaagent/build.gradle.kts b/instrumentation/gwt-2.0/javaagent/build.gradle.kts index 95edcf95c4d0..14d65d0e7a55 100644 --- a/instrumentation/gwt-2.0/javaagent/build.gradle.kts +++ b/instrumentation/gwt-2.0/javaagent/build.gradle.kts @@ -115,6 +115,22 @@ tasks { usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) } + + val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath.plus(files(layout.buildDirectory.dir("testapp/classes"))) + + dependsOn(sourceSets["testapp"].output) + dependsOn(copyTestWebapp) + + usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) + + jvmArgs("-Dotel.semconv-stability.opt-in=rpc") + } + + check { + dependsOn(testStableSemconv) + } } tasks.withType().configureEach { diff --git a/instrumentation/gwt-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtTest.java b/instrumentation/gwt-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtTest.java index 6ddd5a204dc3..112af1939f50 100644 --- a/instrumentation/gwt-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtTest.java +++ b/instrumentation/gwt-2.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtTest.java @@ -5,21 +5,22 @@ package io.opentelemetry.javaagent.instrumentation.gwt; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; import static io.opentelemetry.instrumentation.testing.util.TelemetryDataUtil.orderByRootSpanName; -import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; import static org.junit.jupiter.api.Assertions.assertEquals; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.test.utils.PortUtils; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import java.io.File; import java.io.IOException; import java.net.URI; import java.time.Duration; +import java.util.ArrayList; +import java.util.List; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; @@ -153,14 +154,16 @@ void testGwt() { span.hasName("POST " + getContextPath() + "/greeting/greet") .hasKind(SpanKind.SERVER) .hasNoParent(), - span -> - span.hasName("test.gwt.shared.MessageService/sendMessage") - .hasKind(SpanKind.SERVER) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(RPC_SYSTEM, "gwt"), - equalTo(RPC_SERVICE, "test.gwt.shared.MessageService"), - equalTo(RPC_METHOD, "sendMessage")))); + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("gwt")); + attrs.addAll( + rpcMethodAssertions("test.gwt.shared.MessageService", "sendMessage")); + span.hasName("test.gwt.shared.MessageService/sendMessage") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfying(attrs); + })); testing.clearData(); @@ -175,15 +178,17 @@ void testGwt() { span.hasName("POST " + getContextPath() + "/greeting/greet") .hasKind(SpanKind.SERVER) .hasNoParent(), - span -> - span.hasName("test.gwt.shared.MessageService/sendMessage") - .hasKind(SpanKind.SERVER) - .hasParent(trace.getSpan(0)) - .hasException(new IOException()) - .hasAttributesSatisfyingExactly( - equalTo(RPC_SYSTEM, "gwt"), - equalTo(RPC_SERVICE, "test.gwt.shared.MessageService"), - equalTo(RPC_METHOD, "sendMessage")))); + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("gwt")); + attrs.addAll( + rpcMethodAssertions("test.gwt.shared.MessageService", "sendMessage")); + span.hasName("test.gwt.shared.MessageService/sendMessage") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(0)) + .hasException(new IOException()) + .hasAttributesSatisfying(attrs); + })); driver.close(); } diff --git a/instrumentation/rmi/javaagent/build.gradle.kts b/instrumentation/rmi/javaagent/build.gradle.kts index fbd584f7d293..afaef175ce0f 100644 --- a/instrumentation/rmi/javaagent/build.gradle.kts +++ b/instrumentation/rmi/javaagent/build.gradle.kts @@ -35,4 +35,15 @@ tasks { withType().configureEach { jvmArgs("-Djava.rmi.server.hostname=127.0.0.1") } + + val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + jvmArgs("-Dotel.semconv-stability.opt-in=rpc") + } + + check { + dependsOn(testStableSemconv) + } } diff --git a/instrumentation/rmi/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/rmi/RmiTest.java b/instrumentation/rmi/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/rmi/RmiTest.java index 37db40732287..3705b74bc193 100644 --- a/instrumentation/rmi/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/rmi/RmiTest.java +++ b/instrumentation/rmi/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/rmi/RmiTest.java @@ -5,15 +5,14 @@ package io.opentelemetry.javaagent.instrumentation.rmi; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; 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.ExceptionAttributes.EXCEPTION_MESSAGE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -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.catchThrowableOfType; import io.opentelemetry.api.trace.SpanId; @@ -21,9 +20,12 @@ import io.opentelemetry.instrumentation.test.utils.PortUtils; import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; +import java.util.ArrayList; +import java.util.List; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -79,23 +81,25 @@ void clientCallCreatesSpans() throws Exception { .hasName("parent") .hasKind(SpanKind.INTERNAL) .hasParentSpanId(SpanId.getInvalid()), - span -> - assertThat(span) - .hasName("rmi.app.Greeter/hello") - .hasKind(SpanKind.CLIENT) - .hasParentSpanId(trace.get(0).getSpanId()) - .hasAttributesSatisfyingExactly( - equalTo(RPC_SYSTEM, "java_rmi"), - equalTo(RPC_SERVICE, "rmi.app.Greeter"), - equalTo(RPC_METHOD, "hello")), - span -> - assertThat(span) - .hasName("rmi.app.Server/hello") - .hasKind(SpanKind.SERVER) - .hasAttributesSatisfyingExactly( - equalTo(RPC_SYSTEM, "java_rmi"), - equalTo(RPC_SERVICE, "rmi.app.Server"), - equalTo(RPC_METHOD, "hello")))); + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("java_rmi")); + attrs.addAll(rpcMethodAssertions("rmi.app.Greeter", "hello")); + assertThat(span) + .hasName("rmi.app.Greeter/hello") + .hasKind(SpanKind.CLIENT) + .hasParentSpanId(trace.get(0).getSpanId()) + .hasAttributesSatisfying(attrs); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("java_rmi")); + attrs.addAll(rpcMethodAssertions("rmi.app.Server", "hello")); + assertThat(span) + .hasName("rmi.app.Server/hello") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfying(attrs); + })); } @Test @@ -141,44 +145,46 @@ void serviceThrownException() throws Exception { .hasName("parent") .hasKind(SpanKind.INTERNAL) .hasParentSpanId(SpanId.getInvalid()), - span -> - assertThat(span) - .hasName("rmi.app.Greeter/exceptional") - .hasKind(SpanKind.CLIENT) - .hasParentSpanId(trace.get(0).getSpanId()) - .hasEventsSatisfyingExactly( - event -> - event - .hasName("exception") - .hasAttributesSatisfyingExactly( - equalTo( - EXCEPTION_TYPE, - thrown.getClass().getCanonicalName()), - equalTo(EXCEPTION_MESSAGE, thrown.getMessage()), - satisfies( - EXCEPTION_STACKTRACE, val -> val.isNotNull()))) - .hasAttributesSatisfyingExactly( - equalTo(RPC_SYSTEM, "java_rmi"), - equalTo(RPC_SERVICE, "rmi.app.Greeter"), - equalTo(RPC_METHOD, "exceptional")), - span -> - assertThat(span) - .hasName("rmi.app.Server/exceptional") - .hasKind(SpanKind.SERVER) - .hasEventsSatisfyingExactly( - event -> - event - .hasName("exception") - .hasAttributesSatisfyingExactly( - equalTo( - EXCEPTION_TYPE, - thrown.getClass().getCanonicalName()), - equalTo(EXCEPTION_MESSAGE, thrown.getMessage()), - satisfies( - EXCEPTION_STACKTRACE, val -> val.isNotNull()))) - .hasAttributesSatisfyingExactly( - equalTo(RPC_SYSTEM, "java_rmi"), - equalTo(RPC_SERVICE, "rmi.app.Server"), - equalTo(RPC_METHOD, "exceptional")))); + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("java_rmi")); + attrs.addAll(rpcMethodAssertions("rmi.app.Greeter", "exceptional")); + assertThat(span) + .hasName("rmi.app.Greeter/exceptional") + .hasKind(SpanKind.CLIENT) + .hasParentSpanId(trace.get(0).getSpanId()) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("exception") + .hasAttributesSatisfyingExactly( + equalTo( + EXCEPTION_TYPE, + thrown.getClass().getCanonicalName()), + equalTo(EXCEPTION_MESSAGE, thrown.getMessage()), + satisfies( + EXCEPTION_STACKTRACE, val -> val.isNotNull()))) + .hasAttributesSatisfying(attrs); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("java_rmi")); + attrs.addAll(rpcMethodAssertions("rmi.app.Server", "exceptional")); + assertThat(span) + .hasName("rmi.app.Server/exceptional") + .hasKind(SpanKind.SERVER) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("exception") + .hasAttributesSatisfyingExactly( + equalTo( + EXCEPTION_TYPE, + thrown.getClass().getCanonicalName()), + equalTo(EXCEPTION_MESSAGE, thrown.getMessage()), + satisfies( + EXCEPTION_STACKTRACE, val -> val.isNotNull()))) + .hasAttributesSatisfying(attrs); + })); } } diff --git a/instrumentation/spring/spring-cloud-aws-3.0/javaagent/build.gradle.kts b/instrumentation/spring/spring-cloud-aws-3.0/javaagent/build.gradle.kts index 42d29666160b..2375a116f934 100644 --- a/instrumentation/spring/spring-cloud-aws-3.0/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-cloud-aws-3.0/javaagent/build.gradle.kts @@ -32,6 +32,19 @@ otelJava { minJavaVersionSupported.set(JavaVersion.VERSION_17) } +tasks { + val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + jvmArgs("-Dotel.semconv-stability.opt-in=rpc") + } + + check { + dependsOn(testStableSemconv) + } +} + if (findProperty("denyUnsafe") as Boolean) { // org.elasticmq:elasticmq-rest-sqs_2.13 uses unsafe. Future versions are likely to fix this. tasks.withType().configureEach { diff --git a/instrumentation/spring/spring-cloud-aws-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/aws/AwsSqsTest.java b/instrumentation/spring/spring-cloud-aws-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/aws/AwsSqsTest.java index 4ea234d3ba00..54b19cca02c9 100644 --- a/instrumentation/spring/spring-cloud-aws-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/aws/AwsSqsTest.java +++ b/instrumentation/spring/spring-cloud-aws-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/aws/AwsSqsTest.java @@ -5,6 +5,8 @@ package io.opentelemetry.javaagent.instrumentation.spring.aws; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; @@ -18,17 +20,17 @@ import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_MESSAGE_ID; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -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; import io.awspring.cloud.sqs.operations.SqsTemplate; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.semconv.HttpAttributes; import io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -86,106 +88,108 @@ void sqsListener() throws InterruptedException, ExecutionException, TimeoutExcep trace -> trace.hasSpansSatisfyingExactly( span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), - span -> - span.hasName("Sqs.GetQueueUrl") - .hasKind(SpanKind.CLIENT) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_METHOD, "GetQueueUrl"), - equalTo(RPC_SERVICE, "Sqs"), - equalTo( - HTTP_REQUEST_METHOD, HttpAttributes.HttpRequestMethodValues.POST), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, AwsSqsTestApplication.sqsPort), - satisfies( - URL_FULL, - v -> - v.startsWith( - "http://localhost:" + AwsSqsTestApplication.sqsPort)), - satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))), - span -> - span.hasName("test-queue publish") - .hasKind(SpanKind.PRODUCER) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfyingExactly( - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_METHOD, "SendMessage"), - equalTo(RPC_SERVICE, "Sqs"), - equalTo( - HTTP_REQUEST_METHOD, HttpAttributes.HttpRequestMethodValues.POST), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, AwsSqsTestApplication.sqsPort), - satisfies( - URL_FULL, - v -> - v.startsWith( - "http://localhost:" + AwsSqsTestApplication.sqsPort)), - equalTo( - MESSAGING_SYSTEM, - MessagingIncubatingAttributes.MessagingSystemIncubatingValues - .AWS_SQS), - satisfies(MESSAGING_MESSAGE_ID, AbstractStringAssert::isNotBlank), - equalTo(MESSAGING_OPERATION, "publish"), - equalTo(MESSAGING_DESTINATION_NAME, "test-queue"), - equalTo( - AWS_SQS_QUEUE_URL, - "http://localhost:" - + AwsSqsTestApplication.sqsPort - + "/000000000000/test-queue"), - satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))), - span -> - span.hasName("test-queue process") - .hasKind(SpanKind.CONSUMER) - .hasParent(trace.getSpan(2)) - .hasAttributesSatisfyingExactly( - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_METHOD, "ReceiveMessage"), - equalTo(RPC_SERVICE, "Sqs"), - equalTo( - HTTP_REQUEST_METHOD, HttpAttributes.HttpRequestMethodValues.POST), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, AwsSqsTestApplication.sqsPort), - satisfies( - URL_FULL, - v -> - v.startsWith( - "http://localhost:" + AwsSqsTestApplication.sqsPort)), - equalTo( - MESSAGING_SYSTEM, - MessagingIncubatingAttributes.MessagingSystemIncubatingValues - .AWS_SQS), - satisfies(MESSAGING_MESSAGE_ID, AbstractStringAssert::isNotBlank), - equalTo(MESSAGING_OPERATION, "process"), - equalTo(MESSAGING_DESTINATION_NAME, "test-queue")), + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("Sqs", "GetQueueUrl")); + attrs.add( + equalTo(HTTP_REQUEST_METHOD, HttpAttributes.HttpRequestMethodValues.POST)); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, AwsSqsTestApplication.sqsPort)); + attrs.add( + satisfies( + URL_FULL, + v -> v.startsWith("http://localhost:" + AwsSqsTestApplication.sqsPort))); + attrs.add(satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + span.hasName("Sqs.GetQueueUrl") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(attrs); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("Sqs", "SendMessage")); + attrs.add( + equalTo(HTTP_REQUEST_METHOD, HttpAttributes.HttpRequestMethodValues.POST)); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, AwsSqsTestApplication.sqsPort)); + attrs.add( + satisfies( + URL_FULL, + v -> v.startsWith("http://localhost:" + AwsSqsTestApplication.sqsPort))); + attrs.add( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues.AWS_SQS)); + attrs.add(satisfies(MESSAGING_MESSAGE_ID, AbstractStringAssert::isNotBlank)); + attrs.add(equalTo(MESSAGING_OPERATION, "publish")); + attrs.add(equalTo(MESSAGING_DESTINATION_NAME, "test-queue")); + attrs.add( + equalTo( + AWS_SQS_QUEUE_URL, + "http://localhost:" + + AwsSqsTestApplication.sqsPort + + "/000000000000/test-queue")); + attrs.add(satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + span.hasName("test-queue publish") + .hasKind(SpanKind.PRODUCER) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly(attrs); + }, + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("Sqs", "ReceiveMessage")); + attrs.add( + equalTo(HTTP_REQUEST_METHOD, HttpAttributes.HttpRequestMethodValues.POST)); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, AwsSqsTestApplication.sqsPort)); + attrs.add( + satisfies( + URL_FULL, + v -> v.startsWith("http://localhost:" + AwsSqsTestApplication.sqsPort))); + attrs.add( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues.AWS_SQS)); + attrs.add(satisfies(MESSAGING_MESSAGE_ID, AbstractStringAssert::isNotBlank)); + attrs.add(equalTo(MESSAGING_OPERATION, "process")); + attrs.add(equalTo(MESSAGING_DESTINATION_NAME, "test-queue")); + span.hasName("test-queue process") + .hasKind(SpanKind.CONSUMER) + .hasParent(trace.getSpan(2)) + .hasAttributesSatisfyingExactly(attrs); + }, span -> span.hasName("callback").hasKind(SpanKind.INTERNAL).hasParent(trace.getSpan(3)), - span -> - span.hasName("Sqs.DeleteMessageBatch") - .hasKind(SpanKind.CLIENT) - .hasParent(trace.getSpan(2)) - .hasAttributesSatisfyingExactly( - equalTo(RPC_SYSTEM, "aws-api"), - equalTo(RPC_METHOD, "DeleteMessageBatch"), - equalTo(RPC_SERVICE, "Sqs"), - equalTo( - HTTP_REQUEST_METHOD, HttpAttributes.HttpRequestMethodValues.POST), - equalTo(HTTP_RESPONSE_STATUS_CODE, 200), - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, AwsSqsTestApplication.sqsPort), - satisfies( - URL_FULL, - v -> - v.startsWith( - "http://localhost:" + AwsSqsTestApplication.sqsPort)), - equalTo( - AWS_SQS_QUEUE_URL, - "http://localhost:" - + AwsSqsTestApplication.sqsPort - + "/000000000000/test-queue"), - satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))))); + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("aws-api")); + attrs.addAll(rpcMethodAssertions("Sqs", "DeleteMessageBatch")); + attrs.add( + equalTo(HTTP_REQUEST_METHOD, HttpAttributes.HttpRequestMethodValues.POST)); + attrs.add(equalTo(HTTP_RESPONSE_STATUS_CODE, 200)); + attrs.add(equalTo(SERVER_ADDRESS, "localhost")); + attrs.add(equalTo(SERVER_PORT, AwsSqsTestApplication.sqsPort)); + attrs.add( + satisfies( + URL_FULL, + v -> v.startsWith("http://localhost:" + AwsSqsTestApplication.sqsPort))); + attrs.add( + equalTo( + AWS_SQS_QUEUE_URL, + "http://localhost:" + + AwsSqsTestApplication.sqsPort + + "/000000000000/test-queue")); + attrs.add(satisfies(AWS_REQUEST_ID, val -> val.isInstanceOf(String.class))); + span.hasName("Sqs.DeleteMessageBatch") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(2)) + .hasAttributesSatisfyingExactly(attrs); + })); } } diff --git a/instrumentation/spring/spring-rmi-4.0/javaagent/build.gradle.kts b/instrumentation/spring/spring-rmi-4.0/javaagent/build.gradle.kts index 8769e812cb11..60940979b7ef 100644 --- a/instrumentation/spring/spring-rmi-4.0/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-rmi-4.0/javaagent/build.gradle.kts @@ -34,9 +34,22 @@ otelJava { maxJavaVersionForTests.set(JavaVersion.VERSION_23) } -tasks.withType().configureEach { - jvmArgs("-Djava.rmi.server.hostname=127.0.0.1") - systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") +tasks { + val testStableSemconv by registering(Test::class) { + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + jvmArgs("-Dotel.semconv-stability.opt-in=rpc") + } + + check { + dependsOn(testStableSemconv) + } + + withType().configureEach { + jvmArgs("-Djava.rmi.server.hostname=127.0.0.1") + systemProperty("collectMetadata", findProperty("collectMetadata")?.toString() ?: "false") + } } configurations.testRuntimeClasspath { diff --git a/instrumentation/spring/spring-rmi-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/rmi/v4_0/SpringRmiTest.java b/instrumentation/spring/spring-rmi-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/rmi/v4_0/SpringRmiTest.java index 4d1570932d02..ad48831d51e4 100644 --- a/instrumentation/spring/spring-rmi-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/rmi/v4_0/SpringRmiTest.java +++ b/instrumentation/spring/spring-rmi-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/rmi/v4_0/SpringRmiTest.java @@ -5,14 +5,13 @@ package io.opentelemetry.javaagent.instrumentation.spring.rmi.v4_0; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcMethodAssertions; +import static io.opentelemetry.instrumentation.testing.junit.rpc.RpcSemconvStabilityUtil.rpcSystemAssertion; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_METHOD; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; -import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -20,6 +19,7 @@ import io.opentelemetry.instrumentation.test.utils.PortUtils; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; import io.opentelemetry.sdk.trace.data.StatusData; import java.rmi.RemoteException; @@ -155,24 +155,26 @@ void clientCallCreatesSpans(TestSource testSource) throws RemoteException { List> assertions = new ArrayList<>(); assertions.add(span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent()); assertions.add( - span -> - span.hasName("springrmi.app.SpringRmiGreeter/hello") - .hasKind(SpanKind.CLIENT) - .hasParent(trace.getSpan(0)) - .hasAttributesSatisfying( - equalTo(RPC_SYSTEM, "spring_rmi"), - equalTo(RPC_SERVICE, "springrmi.app.SpringRmiGreeter"), - equalTo(RPC_METHOD, "hello"))); + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("spring_rmi")); + attrs.addAll(rpcMethodAssertions("springrmi.app.SpringRmiGreeter", "hello")); + span.hasName("springrmi.app.SpringRmiGreeter/hello") + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfying(attrs); + }); if (testSource == TestSource.RMI) { assertions.add( - span -> - span.hasName(testSource.remoteClassName + "/hello") - .hasKind(SpanKind.SERVER) - .hasParent(trace.getSpan(1)) - .hasAttributesSatisfying( - equalTo(RPC_SYSTEM, testSource.serverSystem), - equalTo(RPC_SERVICE, testSource.remoteClassName), - equalTo(RPC_METHOD, "hello"))); + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion(testSource.serverSystem)); + attrs.addAll(rpcMethodAssertions(testSource.remoteClassName, "hello")); + span.hasName(testSource.remoteClassName + "/hello") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(1)) + .hasAttributesSatisfying(attrs); + }); } trace.hasSpansSatisfyingExactly(assertions); @@ -206,11 +208,37 @@ void throwsException(TestSource testSource) { EXCEPTION_STACKTRACE, val -> val.isInstanceOf(String.class))))); assertions.add( - span -> - span.hasName("springrmi.app.SpringRmiGreeter/exceptional") - .hasKind(SpanKind.CLIENT) + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion("spring_rmi")); + attrs.addAll(rpcMethodAssertions("springrmi.app.SpringRmiGreeter", "exceptional")); + span.hasName("springrmi.app.SpringRmiGreeter/exceptional") + .hasKind(SpanKind.CLIENT) + .hasStatus(StatusData.error()) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfying(attrs) + .hasEventsSatisfyingExactly( + event -> + event + .hasName("exception") + .hasAttributesSatisfying( + equalTo(EXCEPTION_TYPE, error.getClass().getCanonicalName()), + equalTo(EXCEPTION_MESSAGE, error.getMessage()), + satisfies( + EXCEPTION_STACKTRACE, + val -> val.isInstanceOf(String.class)))); + }); + if (testSource == TestSource.RMI) { + assertions.add( + span -> { + List attrs = new ArrayList<>(); + attrs.add(rpcSystemAssertion(testSource.serverSystem)); + attrs.addAll(rpcMethodAssertions(testSource.remoteClassName, "exceptional")); + span.hasName(testSource.remoteClassName + "/exceptional") + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(1)) .hasStatus(StatusData.error()) - .hasParent(trace.getSpan(0)) + .hasAttributesSatisfying(attrs) .hasEventsSatisfyingExactly( event -> event @@ -220,33 +248,8 @@ void throwsException(TestSource testSource) { equalTo(EXCEPTION_MESSAGE, error.getMessage()), satisfies( EXCEPTION_STACKTRACE, - val -> val.isInstanceOf(String.class)))) - .hasAttributesSatisfying( - equalTo(RPC_SYSTEM, "spring_rmi"), - equalTo(RPC_SERVICE, "springrmi.app.SpringRmiGreeter"), - equalTo(RPC_METHOD, "exceptional"))); - if (testSource == TestSource.RMI) { - assertions.add( - span -> - span.hasName(testSource.remoteClassName + "/exceptional") - .hasKind(SpanKind.SERVER) - .hasParent(trace.getSpan(1)) - .hasStatus(StatusData.error()) - .hasEventsSatisfyingExactly( - event -> - event - .hasName("exception") - .hasAttributesSatisfying( - equalTo( - EXCEPTION_TYPE, error.getClass().getCanonicalName()), - equalTo(EXCEPTION_MESSAGE, error.getMessage()), - satisfies( - EXCEPTION_STACKTRACE, - val -> val.isInstanceOf(String.class)))) - .hasAttributesSatisfying( - equalTo(RPC_SYSTEM, testSource.serverSystem), - equalTo(RPC_SERVICE, testSource.remoteClassName), - equalTo(RPC_METHOD, "exceptional"))); + val -> val.isInstanceOf(String.class)))); + }); } trace.hasSpansSatisfyingExactly(assertions); diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/rpc/RpcSemconvStabilityUtil.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/rpc/RpcSemconvStabilityUtil.java new file mode 100644 index 000000000000..5684397e91a7 --- /dev/null +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/rpc/RpcSemconvStabilityUtil.java @@ -0,0 +1,138 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.testing.junit.rpc; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SERVICE; +import static io.opentelemetry.semconv.incubating.RpcIncubatingAttributes.RPC_SYSTEM; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import org.assertj.core.api.AbstractStringAssert; + +// until old rpc semconv are dropped in 3.0 +public class RpcSemconvStabilityUtil { + + // Stable semconv keys - need to reference from the extractors since they're not public in semconv + private static final AttributeKey RPC_SYSTEM_NAME = + AttributeKey.stringKey("rpc.system.name"); + private static final AttributeKey RPC_METHOD = AttributeKey.stringKey("rpc.method"); + + private RpcSemconvStabilityUtil() {} + + /** + * Returns RPC method attribute assertions that work for both old and stable semconv. Pass both + * service and method parameters, and the helper will return the appropriate assertions based on + * the semconv mode. + * + *

Note: In dup mode, old spans use rpc.method.deprecated to avoid collision with stable + * rpc.method. + * + * @param service The RPC service name (e.g., "my.Service") + * @param method The RPC method name (e.g., "Method") + * @return List of attribute assertions for the method + */ + public static List rpcMethodAssertions(String service, String method) { + return rpcMethodAssertions(service, method, (a, expected) -> a.isEqualTo(expected)); + } + + public static List rpcMethodAssertions( + String service, + String method, + BiConsumer, String> serviceNameAssertion) { + List assertions = new ArrayList<>(); + + if (SemconvStability.emitStableRpcSemconv()) { + assertions.add( + satisfies(RPC_METHOD, a -> serviceNameAssertion.accept(a, service + "/" + method))); + } else { + assertions.add(equalTo(RPC_METHOD, method)); + assertions.add(satisfies(RPC_SERVICE, a -> serviceNameAssertion.accept(a, service))); + } + + return assertions; + } + + /** + * Returns RPC system attribute assertion that works for both old and stable semconv. + * + * @param systemName The RPC system name (e.g., "grpc", "apache_dubbo") + * @return Attribute assertion for the system + */ + public static AttributeAssertion rpcSystemAssertion(String systemName) { + if (SemconvStability.emitStableRpcSemconv()) { + return equalTo(RPC_SYSTEM_NAME, SemconvStability.stableRpcSystemName(systemName)); + } + return equalTo(RPC_SYSTEM, systemName); + } + + /** + * Returns the server duration metric name based on semconv mode. + * + * @return "rpc.server.call.duration" for stable, "rpc.server.duration" for old + */ + public static String getServerDurationMetricName() { + return SemconvStability.emitStableRpcSemconv() + ? "rpc.server.call.duration" + : "rpc.server.duration"; + } + + /** + * Returns the client duration metric name based on semconv mode. + * + * @return "rpc.client.call.duration" for stable, "rpc.client.duration" for old + */ + public static String getClientDurationMetricName() { + return SemconvStability.emitStableRpcSemconv() + ? "rpc.client.call.duration" + : "rpc.client.duration"; + } + + /** + * Returns the duration unit based on semconv mode. + * + * @return "s" (seconds) for stable, "ms" (milliseconds) for old + */ + public static String getDurationUnit() { + return SemconvStability.emitStableRpcSemconv() ? "s" : "ms"; + } + + /** + * Returns an attribute assertion for gRPC status code based on semconv mode. In stable semconv, + * rpc.grpc.status_code (Long) is replaced with rpc.response.status_code (String). + * + * @param statusCode The status code value + * @return Attribute assertion for the status code + */ + public static AttributeAssertion grpcStatusCodeAssertion(long statusCode) { + if (SemconvStability.emitStableRpcSemconv()) { + return equalTo( + AttributeKey.stringKey("rpc.response.status_code"), String.valueOf(statusCode)); + } + return equalTo(AttributeKey.longKey("rpc.grpc.status_code"), statusCode); + } + + /** + * Returns an attribute assertion for error.type if in stable semconv mode. In stable semconv, + * error.type is automatically added when a span has an error status. Returns a list containing + * the assertion, or an empty list if not in stable mode or no exception. + * + * @param exceptionClassName The full exception class name (e.g., "java.lang.RuntimeException") + * @return List containing error.type assertion if in stable mode, empty list otherwise + */ + public static List errorTypeAssertion(String exceptionClassName) { + List assertions = new ArrayList<>(); + if (SemconvStability.emitStableRpcSemconv() && exceptionClassName != null) { + assertions.add(equalTo(AttributeKey.stringKey("error.type"), exceptionClassName)); + } + return assertions; + } +}