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 a895dfa68d85..3caa6d72f4a9 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 @@ -5,7 +5,12 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.rpc; +import static io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcCommonAttributesExtractor.RPC_METHOD; +import static io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcMetricsContextCustomizers.OLD_RPC_METHOD_CONTEXT_KEY; +import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitOldRpcSemconv; +import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableRpcSemconv; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.logging.Level.FINE; import com.google.auto.value.AutoValue; @@ -21,6 +26,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics; import io.opentelemetry.instrumentation.api.internal.OperationMetricsUtil; 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; + @Nullable private final LongHistogram oldClientRequestSize; + @Nullable 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(); - - LongHistogramBuilder requestSizeBuilder = - meter - .histogramBuilder("rpc.client.request.size") - .setUnit("By") - .setDescription("Measures the size of RPC request messages (uncompressed).") - .ofLongs(); - RpcMetricsAdvice.applyClientRequestSizeAdvice(requestSizeBuilder); - clientRequestSize = requestSizeBuilder.build(); - - LongHistogramBuilder responseSizeBuilder = - meter - .histogramBuilder("rpc.client.response.size") - .setUnit("By") - .setDescription("Measures the size of RPC response messages (uncompressed).") - .ofLongs(); - RpcMetricsAdvice.applyClientRequestSizeAdvice(responseSizeBuilder); - clientResponseSize = responseSizeBuilder.build(); + // Old metric (milliseconds) + if (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 (emitStableRpcSemconv()) { + DoubleHistogramBuilder stableDurationBuilder = + meter + .histogramBuilder("rpc.client.call.duration") + .setDescription("Measures the duration of outbound remote procedure calls (RPC).") + .setUnit("s"); + RpcMetricsAdvice.applyClientDurationAdvice(stableDurationBuilder, true); + stableClientDurationHistogram = stableDurationBuilder.build(); + } else { + stableClientDurationHistogram = null; + } + + if (emitOldRpcSemconv()) { + LongHistogramBuilder requestSizeBuilder = + meter + .histogramBuilder("rpc.client.request.size") + .setUnit("By") + .setDescription("Measures the size of RPC request messages (uncompressed).") + .ofLongs(); + RpcMetricsAdvice.applyOldClientRequestSizeAdvice(requestSizeBuilder); + oldClientRequestSize = requestSizeBuilder.build(); + + LongHistogramBuilder responseSizeBuilder = + meter + .histogramBuilder("rpc.client.response.size") + .setUnit("By") + .setDescription("Measures the size of RPC response messages (uncompressed).") + .ofLongs(); + RpcMetricsAdvice.applyOldClientRequestSizeAdvice(responseSizeBuilder); + oldClientResponseSize = responseSizeBuilder.build(); + } else { + oldClientRequestSize = null; + oldClientResponseSize = null; + } } /** @@ -81,7 +112,8 @@ public static OperationMetrics get() { public Context onStart(Context context, Attributes startAttributes, long startNanos) { return context.with( RPC_CLIENT_REQUEST_METRICS_STATE, - new AutoValue_RpcClientMetrics_State(startAttributes, startNanos)); + new AutoValue_RpcClientMetrics_State( + startAttributes, startNanos, context.get(OLD_RPC_METHOD_CONTEXT_KEY))); } @Override @@ -95,18 +127,38 @@ 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(); + + if (emitOldRpcSemconv()) { + Attributes oldAttributes = getOldAttributes(attributes, state); - Long rpcClientRequestBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_REQUEST_SIZE); - if (rpcClientRequestBodySize != null) { - clientRequestSize.record(rpcClientRequestBodySize, attributes, context); + if (oldClientDurationHistogram != null) { + oldClientDurationHistogram.record(durationNanos / NANOS_PER_MS, oldAttributes, context); + } + + Long rpcClientRequestBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_REQUEST_SIZE); + if (oldClientRequestSize != null && rpcClientRequestBodySize != null) { + oldClientRequestSize.record(rpcClientRequestBodySize, oldAttributes, context); + } + + Long rpcClientResponseBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_RESPONSE_SIZE); + if (oldClientResponseSize != null && rpcClientResponseBodySize != null) { + oldClientResponseSize.record(rpcClientResponseBodySize, oldAttributes, context); + } + } + + 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); + private static Attributes getOldAttributes(Attributes attributes, State state) { + String oldRpcMethod = state.oldRpcMethod(); + if (oldRpcMethod != null) { + // dup mode: replace stable rpc.method with old value + return attributes.toBuilder().put(RPC_METHOD, oldRpcMethod).build(); } + return attributes; } @AutoValue @@ -115,5 +167,8 @@ abstract static class State { abstract Attributes startAttributes(); abstract long startTimeNanos(); + + @Nullable + abstract String oldRpcMethod(); } } 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 43f6da77659c..555a1bf977cd 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 @@ -9,65 +9,105 @@ 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 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 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, - NETWORK_TYPE, - NETWORK_TRANSPORT, - SERVER_ADDRESS, - SERVER_PORT); - - static void applyClientDurationAdvice(DoubleHistogramBuilder builder) { + + private static final List> RPC_METRICS_OLD_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 + keys.add( + stable + ? RpcCommonAttributesExtractor.RPC_SYSTEM_NAME + : RpcCommonAttributesExtractor.RPC_SYSTEM); + + // Add RPC service (old only) + if (!stable) { + keys.add(RpcCommonAttributesExtractor.RPC_SERVICE); + } + + keys.add(RpcCommonAttributesExtractor.RPC_METHOD); + + if (stable) { + keys.add(RPC_RESPONSE_STATUS_CODE); + } else { + keys.add(RPC_GRPC_STATUS_CODE); + } + + // Network type and transport only for old semconv + if (!stable) { + keys.add(NETWORK_TYPE); + keys.add(NETWORK_TRANSPORT); + } + + // Common attributes + keys.add(SERVER_ADDRESS); + keys.add(SERVER_PORT); + + return keys; + } + + private static List> getAttributeKeys(boolean stable) { + return stable ? RPC_METRICS_STABLE_ATTRIBUTE_KEYS : RPC_METRICS_OLD_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 applyOldClientRequestSizeAdvice(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_OLD_ATTRIBUTE_KEYS); } - static void applyServerRequestSizeAdvice(LongHistogramBuilder builder) { + static void applyOldServerRequestSizeAdvice(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_OLD_ATTRIBUTE_KEYS); } private RpcMetricsAdvice() {} diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcMetricsContextCustomizers.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcMetricsContextCustomizers.java new file mode 100644 index 000000000000..e0c0dc13a644 --- /dev/null +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcMetricsContextCustomizers.java @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.incubator.semconv.rpc; + +import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitOldRpcSemconv; +import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableRpcSemconv; + +import io.opentelemetry.context.ContextKey; +import io.opentelemetry.instrumentation.api.instrumenter.ContextCustomizer; + +/** + * Provides {@link ContextCustomizer} instances for RPC metrics dual-semconv support. + * + * @deprecated This class is only needed during the transition period when both old and stable RPC + * semantic conventions are emitted simultaneously. + */ +@Deprecated // to be removed in 3.0 +public final class RpcMetricsContextCustomizers { + + static final ContextKey OLD_RPC_METHOD_CONTEXT_KEY = + ContextKey.named("otel-rpc-old-method"); + + /** + * Returns a {@link ContextCustomizer} that captures the old {@code rpc.method} value in context + * so that RPC metrics can use it when both old and stable semantic conventions are active. + */ + public static ContextCustomizer dualEmitContextCustomizer( + RpcAttributesGetter getter) { + return (context, request, startAttributes) -> { + if (emitOldRpcSemconv() && emitStableRpcSemconv()) { + String oldMethod = getter.getMethod(request); + if (oldMethod != null) { + return context.with(OLD_RPC_METHOD_CONTEXT_KEY, oldMethod); + } + } + return context; + }; + } + + private RpcMetricsContextCustomizers() {} +} 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 757c2585257d..b96629f44a06 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 @@ -5,7 +5,12 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.rpc; +import static io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcCommonAttributesExtractor.RPC_METHOD; +import static io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcMetricsContextCustomizers.OLD_RPC_METHOD_CONTEXT_KEY; +import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitOldRpcSemconv; +import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableRpcSemconv; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.logging.Level.FINE; import com.google.auto.value.AutoValue; @@ -21,6 +26,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics; import io.opentelemetry.instrumentation.api.internal.OperationMetricsUtil; 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; + @Nullable private final LongHistogram oldServerRequestSize; + @Nullable 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(); - - LongHistogramBuilder requestSizeBuilder = - meter - .histogramBuilder("rpc.server.request.size") - .setUnit("By") - .setDescription("Measures the size of RPC request messages (uncompressed).") - .ofLongs(); - RpcMetricsAdvice.applyServerRequestSizeAdvice(requestSizeBuilder); - serverRequestSize = requestSizeBuilder.build(); - - LongHistogramBuilder responseSizeBuilder = - meter - .histogramBuilder("rpc.server.response.size") - .setUnit("By") - .setDescription("Measures the size of RPC response messages (uncompressed).") - .ofLongs(); - RpcMetricsAdvice.applyServerRequestSizeAdvice(responseSizeBuilder); - serverResponseSize = responseSizeBuilder.build(); + // Old metric (milliseconds) + if (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 (emitStableRpcSemconv()) { + DoubleHistogramBuilder stableDurationBuilder = + meter + .histogramBuilder("rpc.server.call.duration") + .setDescription("Measures the duration of inbound remote procedure calls (RPC).") + .setUnit("s"); + RpcMetricsAdvice.applyServerDurationAdvice(stableDurationBuilder, true); + stableServerDurationHistogram = stableDurationBuilder.build(); + } else { + stableServerDurationHistogram = null; + } + + if (emitOldRpcSemconv()) { + LongHistogramBuilder requestSizeBuilder = + meter + .histogramBuilder("rpc.server.request.size") + .setUnit("By") + .setDescription("Measures the size of RPC request messages (uncompressed).") + .ofLongs(); + RpcMetricsAdvice.applyOldServerRequestSizeAdvice(requestSizeBuilder); + oldServerRequestSize = requestSizeBuilder.build(); + + LongHistogramBuilder responseSizeBuilder = + meter + .histogramBuilder("rpc.server.response.size") + .setUnit("By") + .setDescription("Measures the size of RPC response messages (uncompressed).") + .ofLongs(); + RpcMetricsAdvice.applyOldServerRequestSizeAdvice(responseSizeBuilder); + oldServerResponseSize = responseSizeBuilder.build(); + } else { + oldServerRequestSize = null; + oldServerResponseSize = null; + } } /** @@ -81,7 +112,8 @@ public static OperationMetrics get() { public Context onStart(Context context, Attributes startAttributes, long startNanos) { return context.with( RPC_SERVER_REQUEST_METRICS_STATE, - new AutoValue_RpcServerMetrics_State(startAttributes, startNanos)); + new AutoValue_RpcServerMetrics_State( + startAttributes, startNanos, context.get(OLD_RPC_METHOD_CONTEXT_KEY))); } @Override @@ -95,18 +127,38 @@ 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(); + + if (emitOldRpcSemconv()) { + Attributes oldAttributes = getOldAttributes(attributes, state); - Long rpcServerRequestBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_REQUEST_SIZE); - if (rpcServerRequestBodySize != null) { - serverRequestSize.record(rpcServerRequestBodySize, attributes, context); + if (oldServerDurationHistogram != null) { + oldServerDurationHistogram.record(durationNanos / NANOS_PER_MS, oldAttributes, context); + } + + Long rpcServerRequestBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_REQUEST_SIZE); + if (oldServerRequestSize != null && rpcServerRequestBodySize != null) { + oldServerRequestSize.record(rpcServerRequestBodySize, oldAttributes, context); + } + + Long rpcServerResponseBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_RESPONSE_SIZE); + if (oldServerResponseSize != null && rpcServerResponseBodySize != null) { + oldServerResponseSize.record(rpcServerResponseBodySize, oldAttributes, context); + } + } + + 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); + private static Attributes getOldAttributes(Attributes attributes, State state) { + String oldRpcMethod = state.oldRpcMethod(); + if (oldRpcMethod != null) { + // dup mode: replace stable rpc.method with old value + return attributes.toBuilder().put(RPC_METHOD, oldRpcMethod).build(); } + return attributes; } @AutoValue @@ -115,5 +167,8 @@ abstract static class State { abstract Attributes startAttributes(); abstract long startTimeNanos(); + + @Nullable + abstract String oldRpcMethod(); } } 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 f5c8fc12214e..65e8cc7fe932 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,6 +5,9 @@ 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.instrumentation.api.internal.SemconvStability.emitOldRpcSemconv; +import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableRpcSemconv; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_TRANSPORT; @@ -24,7 +27,9 @@ import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.OperationListener; import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; +import java.util.Collection; import org.junit.jupiter.api.Test; @SuppressWarnings("deprecation") // using deprecated semconv @@ -40,17 +45,23 @@ void collectsMetrics() { Attributes requestAttributes1 = Attributes.builder() - .put(RPC_SYSTEM, "grpc") - .put(RPC_SERVICE, "myservice.EchoService") - .put(RPC_METHOD, "exampleMethod") + .put(RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null) + .put(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null) + .put(RPC_SERVICE, emitOldRpcSemconv() ? "myservice.EchoService" : null) + .put( + RPC_METHOD, + emitStableRpcSemconv() ? "myservice.EchoService/exampleMethod" : "exampleMethod") .put(RpcSizeAttributesExtractor.RPC_REQUEST_SIZE, 10) .build(); Attributes requestAttributes2 = Attributes.builder() - .put(RPC_SYSTEM, "grpc") - .put(RPC_SERVICE, "myservice.EchoService") - .put(RPC_METHOD, "exampleMethod") + .put(RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null) + .put(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null) + .put(RPC_SERVICE, emitOldRpcSemconv() ? "myservice.EchoService" : null) + .put( + RPC_METHOD, + emitStableRpcSemconv() ? "myservice.EchoService/exampleMethod" : "exampleMethod") .build(); Attributes responseAttributes1 = @@ -74,114 +85,203 @@ void collectsMetrics() { "090a0b0c0d0e0f00", TraceFlags.getSampled(), TraceState.getDefault()))); - - Context context1 = listener.onStart(parent, requestAttributes1, nanos(100)); + Context context1 = + listener.onStart( + withDualEmitContextCustomizer(parent, "exampleMethod"), requestAttributes1, nanos(100)); assertThat(metricReader.collectAllMetrics()).isEmpty(); - Context context2 = listener.onStart(Context.root(), requestAttributes2, nanos(150)); + Context context2 = + listener.onStart( + withDualEmitContextCustomizer(Context.root(), "exampleMethod"), + requestAttributes2, + nanos(150)); assertThat(metricReader.collectAllMetrics()).isEmpty(); 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(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "myservice.EchoService"), - equalTo(RPC_METHOD, "exampleMethod"), - equalTo(SERVER_ADDRESS, "example.com"), - equalTo(SERVER_PORT, 8080), - equalTo(NETWORK_TRANSPORT, "tcp"), - equalTo(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(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "myservice.EchoService"), - equalTo(RPC_METHOD, "exampleMethod"), - equalTo(SERVER_ADDRESS, "example.com"), - equalTo(SERVER_PORT, 8080), - equalTo(NETWORK_TRANSPORT, "tcp"), - equalTo(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(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "myservice.EchoService"), - equalTo(RPC_METHOD, "exampleMethod"), - equalTo(SERVER_ADDRESS, "example.com"), - equalTo(SERVER_PORT, 8080), - equalTo(NETWORK_TRANSPORT, "tcp"), - equalTo(NETWORK_TYPE, "ipv4")) - .hasExemplarsSatisfying( - exemplar -> - exemplar - .hasTraceId("ff01020304050600ff0a0b0c0d0e0f00") - .hasSpanId("090a0b0c0d0e0f00"))))); + Collection metrics1 = metricReader.collectAllMetrics(); + + if (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( + equalTo(RPC_SYSTEM, "grpc"), + equalTo(RPC_SERVICE, "myservice.EchoService"), + equalTo(RPC_METHOD, "exampleMethod"), + equalTo(SERVER_ADDRESS, "example.com"), + equalTo(SERVER_PORT, 8080), + equalTo(NETWORK_TRANSPORT, "tcp"), + equalTo(NETWORK_TYPE, "ipv4")) + .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( + equalTo(RPC_SYSTEM, "grpc"), + equalTo(RPC_SERVICE, "myservice.EchoService"), + equalTo(RPC_METHOD, "exampleMethod"), + equalTo(SERVER_ADDRESS, "example.com"), + equalTo(SERVER_PORT, 8080), + equalTo(NETWORK_TRANSPORT, "tcp"), + equalTo(NETWORK_TYPE, "ipv4")) + .hasExemplarsSatisfying( + exemplar -> + exemplar + .hasTraceId( + "ff01020304050600ff0a0b0c0d0e0f00") + .hasSpanId("090a0b0c0d0e0f00"))))) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.client.duration") + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(150 /* millis */) + .hasAttributesSatisfyingExactly( + equalTo(RPC_SYSTEM, "grpc"), + equalTo(RPC_SERVICE, "myservice.EchoService"), + equalTo(RPC_METHOD, "exampleMethod"), + equalTo(SERVER_ADDRESS, "example.com"), + equalTo(SERVER_PORT, 8080), + equalTo(NETWORK_TRANSPORT, "tcp"), + equalTo(NETWORK_TYPE, "ipv4")) + .hasExemplarsSatisfying( + exemplar -> + exemplar + .hasTraceId( + "ff01020304050600ff0a0b0c0d0e0f00") + .hasSpanId("090a0b0c0d0e0f00"))))); + } + + if (emitStableRpcSemconv()) { + assertThat(metrics1) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.client.call.duration") + .hasUnit("s") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(0.15) + .hasAttributesSatisfyingExactly( + equalTo( + RPC_METHOD, + "myservice.EchoService/exampleMethod"), + equalTo(RPC_SYSTEM_NAME, "grpc"), + equalTo(SERVER_ADDRESS, "example.com"), + equalTo(SERVER_PORT, 8080)) + .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(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "myservice.EchoService"), - equalTo(RPC_METHOD, "exampleMethod"), - equalTo(SERVER_PORT, 8080), - equalTo(NETWORK_TRANSPORT, "tcp"))))); + Collection metrics2 = metricReader.collectAllMetrics(); + + if (emitOldRpcSemconv()) { + assertThat(metrics2) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.client.duration") + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(150 /* millis */) + .hasAttributesSatisfyingExactly( + equalTo(RPC_SYSTEM, "grpc"), + equalTo(RPC_SERVICE, "myservice.EchoService"), + equalTo(RPC_METHOD, "exampleMethod"), + equalTo(SERVER_PORT, 8080), + equalTo(NETWORK_TRANSPORT, "tcp"))))); + } + + if (emitStableRpcSemconv()) { + assertThat(metrics2) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.client.call.duration") + .hasUnit("s") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(0.15) + .hasAttributesSatisfyingExactly( + equalTo( + RPC_METHOD, + "myservice.EchoService/exampleMethod"), + equalTo(RPC_SYSTEM_NAME, "grpc"), + equalTo(SERVER_PORT, 8080))))); + } } private static long nanos(int millis) { return MILLISECONDS.toNanos(millis); } + + @SuppressWarnings("deprecation") + private static Context withDualEmitContextCustomizer(Context context, String method) { + return RpcMetricsContextCustomizers.dualEmitContextCustomizer( + new RpcAttributesGetter() { + @Override + public String getSystem(String request) { + return null; + } + + @Override + public String getService(String request) { + return null; + } + + @Override + public String getMethod(String request) { + return request; + } + }) + .onStart(context, method, Attributes.empty()); + } } 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 c34a6a30328a..e6f22371bc9f 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,6 +5,9 @@ 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.instrumentation.api.internal.SemconvStability.emitOldRpcSemconv; +import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableRpcSemconv; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_LOCAL_ADDRESS; @@ -25,7 +28,9 @@ import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.instrumenter.OperationListener; import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; +import java.util.Collection; import org.junit.jupiter.api.Test; @SuppressWarnings("deprecation") // using deprecated semconv @@ -41,17 +46,23 @@ void collectsMetrics() { Attributes requestAttributes1 = Attributes.builder() - .put(RPC_SYSTEM, "grpc") - .put(RPC_SERVICE, "myservice.EchoService") - .put(RPC_METHOD, "exampleMethod") + .put(RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null) + .put(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null) + .put(RPC_SERVICE, emitOldRpcSemconv() ? "myservice.EchoService" : null) + .put( + RPC_METHOD, + emitStableRpcSemconv() ? "myservice.EchoService/exampleMethod" : "exampleMethod") .put(RpcSizeAttributesExtractor.RPC_REQUEST_SIZE, 10) .build(); Attributes requestAttributes2 = Attributes.builder() - .put(RPC_SYSTEM, "grpc") - .put(RPC_SERVICE, "myservice.EchoService") - .put(RPC_METHOD, "exampleMethod") + .put(RPC_SYSTEM_NAME, emitStableRpcSemconv() ? "grpc" : null) + .put(RPC_SYSTEM, emitOldRpcSemconv() ? "grpc" : null) + .put(RPC_SERVICE, emitOldRpcSemconv() ? "myservice.EchoService" : null) + .put( + RPC_METHOD, + emitStableRpcSemconv() ? "myservice.EchoService/exampleMethod" : "exampleMethod") .build(); Attributes responseAttributes1 = @@ -80,114 +91,203 @@ void collectsMetrics() { "090a0b0c0d0e0f00", TraceFlags.getSampled(), TraceState.getDefault()))); - - Context context1 = listener.onStart(parent, requestAttributes1, nanos(100)); + Context context1 = + listener.onStart( + withDualEmitContextCustomizer(parent, "exampleMethod"), requestAttributes1, nanos(100)); assertThat(metricReader.collectAllMetrics()).isEmpty(); - Context context2 = listener.onStart(Context.root(), requestAttributes2, nanos(150)); + Context context2 = + listener.onStart( + withDualEmitContextCustomizer(Context.root(), "exampleMethod"), + requestAttributes2, + nanos(150)); assertThat(metricReader.collectAllMetrics()).isEmpty(); 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(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "myservice.EchoService"), - equalTo(RPC_METHOD, "exampleMethod"), - equalTo(SERVER_ADDRESS, "example.com"), - equalTo(SERVER_PORT, 8080), - equalTo(NETWORK_TRANSPORT, "tcp"), - equalTo(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(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "myservice.EchoService"), - equalTo(RPC_METHOD, "exampleMethod"), - equalTo(SERVER_ADDRESS, "example.com"), - equalTo(SERVER_PORT, 8080), - equalTo(NETWORK_TRANSPORT, "tcp"), - equalTo(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(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "myservice.EchoService"), - equalTo(RPC_METHOD, "exampleMethod"), - equalTo(SERVER_ADDRESS, "example.com"), - equalTo(SERVER_PORT, 8080), - equalTo(NETWORK_TRANSPORT, "tcp"), - equalTo(NETWORK_TYPE, "ipv4")) - .hasExemplarsSatisfying( - exemplar -> - exemplar - .hasTraceId("ff01020304050600ff0a0b0c0d0e0f00") - .hasSpanId("090a0b0c0d0e0f00"))))); + Collection metrics1 = metricReader.collectAllMetrics(); + + if (emitOldRpcSemconv()) { + assertThat(metrics1) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.server.duration") + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(150 /* millis */) + .hasAttributesSatisfyingExactly( + equalTo(RPC_SYSTEM, "grpc"), + equalTo(RPC_SERVICE, "myservice.EchoService"), + equalTo(RPC_METHOD, "exampleMethod"), + equalTo(SERVER_ADDRESS, "example.com"), + equalTo(SERVER_PORT, 8080), + equalTo(NETWORK_TRANSPORT, "tcp"), + equalTo(NETWORK_TYPE, "ipv4")) + .hasExemplarsSatisfying( + exemplar -> + exemplar + .hasTraceId( + "ff01020304050600ff0a0b0c0d0e0f00") + .hasSpanId("090a0b0c0d0e0f00"))))) + .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( + equalTo(RPC_SYSTEM, "grpc"), + equalTo(RPC_SERVICE, "myservice.EchoService"), + equalTo(RPC_METHOD, "exampleMethod"), + equalTo(SERVER_ADDRESS, "example.com"), + equalTo(SERVER_PORT, 8080), + equalTo(NETWORK_TRANSPORT, "tcp"), + equalTo(NETWORK_TYPE, "ipv4")) + .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( + equalTo(RPC_SYSTEM, "grpc"), + equalTo(RPC_SERVICE, "myservice.EchoService"), + equalTo(RPC_METHOD, "exampleMethod"), + equalTo(SERVER_ADDRESS, "example.com"), + equalTo(SERVER_PORT, 8080), + equalTo(NETWORK_TRANSPORT, "tcp"), + equalTo(NETWORK_TYPE, "ipv4")) + .hasExemplarsSatisfying( + exemplar -> + exemplar + .hasTraceId( + "ff01020304050600ff0a0b0c0d0e0f00") + .hasSpanId("090a0b0c0d0e0f00"))))); + } + + if (emitStableRpcSemconv()) { + assertThat(metrics1) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.server.call.duration") + .hasUnit("s") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(0.15) + .hasAttributesSatisfyingExactly( + equalTo( + RPC_METHOD, + "myservice.EchoService/exampleMethod"), + equalTo(RPC_SYSTEM_NAME, "grpc"), + equalTo(SERVER_ADDRESS, "example.com"), + equalTo(SERVER_PORT, 8080)) + .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(RPC_SYSTEM, "grpc"), - equalTo(RPC_SERVICE, "myservice.EchoService"), - equalTo(RPC_METHOD, "exampleMethod"), - equalTo(SERVER_PORT, 8080), - equalTo(NETWORK_TRANSPORT, "tcp"))))); + Collection metrics2 = metricReader.collectAllMetrics(); + + if (emitOldRpcSemconv()) { + assertThat(metrics2) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.server.duration") + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(150 /* millis */) + .hasAttributesSatisfyingExactly( + equalTo(RPC_SYSTEM, "grpc"), + equalTo(RPC_SERVICE, "myservice.EchoService"), + equalTo(RPC_METHOD, "exampleMethod"), + equalTo(SERVER_PORT, 8080), + equalTo(NETWORK_TRANSPORT, "tcp"))))); + } + + if (emitStableRpcSemconv()) { + assertThat(metrics2) + .anySatisfy( + metric -> + assertThat(metric) + .hasName("rpc.server.call.duration") + .hasUnit("s") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(0.15) + .hasAttributesSatisfyingExactly( + equalTo( + RPC_METHOD, + "myservice.EchoService/exampleMethod"), + equalTo(RPC_SYSTEM_NAME, "grpc"), + equalTo(SERVER_PORT, 8080))))); + } } private static long nanos(int millis) { return MILLISECONDS.toNanos(millis); } + + @SuppressWarnings("deprecation") + private static Context withDualEmitContextCustomizer(Context context, String method) { + return RpcMetricsContextCustomizers.dualEmitContextCustomizer( + new RpcAttributesGetter() { + @Override + public String getSystem(String request) { + return null; + } + + @Override + public String getService(String request) { + return null; + } + + @Override + public String getMethod(String request) { + return request; + } + }) + .onStart(context, method, Attributes.empty()); + } }