diff --git a/instrumentation-api-incubator/build.gradle.kts b/instrumentation-api-incubator/build.gradle.kts index 45924c2b1882..2a0d8dc8bd50 100644 --- a/instrumentation-api-incubator/build.gradle.kts +++ b/instrumentation-api-incubator/build.gradle.kts @@ -91,14 +91,14 @@ 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,service.peer") + jvmArgs("-Dotel.semconv-stability.opt-in=database,code,service.peer,rpc") inputs.dir(jflexOutputDir) } 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,service.peer/dup") + jvmArgs("-Dotel.semconv-stability.opt-in=database/dup,code/dup,service.peer/dup,rpc/dup") inputs.dir(jflexOutputDir) } 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..748eac2cc160 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 @@ -14,7 +14,7 @@ * library/framework. It will be used by the {@link RpcClientAttributesExtractor} or {@link * RpcServerAttributesExtractor} to obtain the various RPC attributes in a type-generic way. */ -public interface RpcAttributesGetter { +public interface RpcAttributesGetter { @Nullable String getSystem(REQUEST request); @@ -22,6 +22,10 @@ public interface RpcAttributesGetter { @Nullable String getService(REQUEST request); + /** + * @deprecated Use {@link #getRpcMethod(REQUEST)} for stable semconv. + */ + @Deprecated @Nullable String getMethod(REQUEST request); @@ -34,4 +38,62 @@ default Long getRequestSize(REQUEST request) { default Long getResponseSize(REQUEST request) { return null; } + + /** + * Returns the fully-qualified RPC method name for stable semconv. + * + * @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 getRpcMethod(REQUEST request) { + return null; + } + + /** + * Returns a description of a class of error the operation ended with. + * + *

This method should return {@code null} if there was no error. + * + *

If this method is not implemented, or if it returns {@code null}, the exception class name + * will be used as error type. + * + *

The cardinality of the error type should be low. The instrumentations implementing this + * method are recommended to document the custom values they support. + * + *

Examples: {@code OK}, {@code CANCELLED}, {@code UNKNOWN}, {@code -32602} + */ + @Nullable + default String getErrorType( + REQUEST request, @Nullable RESPONSE response, @Nullable Throwable error) { + return null; + } + + /** + * Returns whether the RPC method is recognized as a predefined method by the RPC framework or + * library. + * + *

Some RPC frameworks or libraries provide a fixed set of recognized methods for client stubs + * and server implementations. Instrumentations for such frameworks MUST return {@code true} only + * when the method is recognized by the framework or library. + * + *

When the method is not recognized (for example, when the server receives a request for a + * method that is not predefined on the server), or when instrumentation is not able to reliably + * detect if the method is predefined, this method MUST return {@code false}. + * + *

When this method returns {@code false}, the {@code rpc.method} attribute will be set to + * {@code "_OTHER"} and the {@code rpc.method_original} attribute will be set to the original + * method name. + * + *

Note: If the RPC instrumentation could end up converting valid RPC methods to {@code + * "_OTHER"}, then it SHOULD provide a way to configure the list of recognized RPC methods. + * + * @param request the request object + * @return {@code true} if the method is recognized as predefined by the framework, {@code false} + * otherwise + */ + default boolean isPredefined(REQUEST request) { + return false; + } } diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcClientAttributesExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcClientAttributesExtractor.java index c0759f237438..31353ce3ffa6 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcClientAttributesExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcClientAttributesExtractor.java @@ -22,11 +22,11 @@ public final class RpcClientAttributesExtractor /** Creates the RPC client attributes extractor. */ public static AttributesExtractor create( - RpcAttributesGetter getter) { + RpcAttributesGetter getter) { return new RpcClientAttributesExtractor<>(getter); } - private RpcClientAttributesExtractor(RpcAttributesGetter getter) { + private RpcClientAttributesExtractor(RpcAttributesGetter getter) { super(getter); } 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..b04f4753de88 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,6 +5,8 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.rpc; +import static io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcCommonAttributesExtractor.OLD_RPC_METHOD_CONTEXT_KEY; +import static io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcCommonAttributesExtractor.RPC_METHOD; import static java.util.logging.Level.FINE; import com.google.auto.value.AutoValue; @@ -19,8 +21,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("Measures the duration of outbound remote procedure calls (RPC).") + .setUnit("s"); + RpcMetricsAdvice.applyClientDurationAdvice(stableDurationBuilder, true); + stableClientDurationHistogram = stableDurationBuilder.build(); + } else { + stableClientDurationHistogram = null; + } LongHistogramBuilder requestSizeBuilder = meter @@ -55,8 +79,8 @@ private RpcClientMetrics(Meter meter) { .setUnit("By") .setDescription("Measures the size of RPC request messages (uncompressed).") .ofLongs(); - RpcMetricsAdvice.applyClientRequestSizeAdvice(requestSizeBuilder); - clientRequestSize = requestSizeBuilder.build(); + RpcMetricsAdvice.applyOldClientRequestSizeAdvice(requestSizeBuilder); + oldClientRequestSize = requestSizeBuilder.build(); LongHistogramBuilder responseSizeBuilder = meter @@ -64,8 +88,8 @@ private RpcClientMetrics(Meter meter) { .setUnit("By") .setDescription("Measures the size of RPC response messages (uncompressed).") .ofLongs(); - RpcMetricsAdvice.applyClientRequestSizeAdvice(responseSizeBuilder); - clientResponseSize = responseSizeBuilder.build(); + RpcMetricsAdvice.applyOldClientRequestSizeAdvice(responseSizeBuilder); + oldClientResponseSize = responseSizeBuilder.build(); } /** @@ -81,7 +105,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 +120,41 @@ 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) { + Attributes oldAttributes = getOldAttributes(attributes, state); + oldClientDurationHistogram.record(durationNanos / NANOS_PER_MS, oldAttributes, 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()) { + Attributes oldAttributes = getOldAttributes(attributes, state); + + Long rpcClientRequestBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_REQUEST_SIZE); + if (rpcClientRequestBodySize != null) { + oldClientRequestSize.record(rpcClientRequestBodySize, oldAttributes, context); + } + + Long rpcClientResponseBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_RESPONSE_SIZE); + if (rpcClientResponseBodySize != null) { + oldClientResponseSize.record(rpcClientResponseBodySize, oldAttributes, 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 +163,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/RpcCommonAttributesExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcCommonAttributesExtractor.java index f731e703586c..33e73d2cfea9 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,32 +6,84 @@ 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.context.ContextKey; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.ContextCustomizer; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; import javax.annotation.Nullable; -abstract class RpcCommonAttributesExtractor +public abstract class RpcCommonAttributesExtractor implements AttributesExtractor { - // copied from RpcIncubatingAttributes static final AttributeKey RPC_METHOD = AttributeKey.stringKey("rpc.method"); + + static final ContextKey OLD_RPC_METHOD_CONTEXT_KEY = + ContextKey.named("otel-rpc-old-method"); + + @SuppressWarnings("deprecation") // for getMethod() + public static ContextCustomizer oldMethodContextCustomizer( + RpcAttributesGetter getter) { + return (context, request, startAttributes) -> { + if (SemconvStability.emitOldRpcSemconv() && SemconvStability.emitStableRpcSemconv()) { + String oldMethod = getter.getMethod(request); + if (oldMethod != null) { + return context.with(OLD_RPC_METHOD_CONTEXT_KEY, oldMethod); + } + } + return context; + }; + } + + // 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; + static final AttributeKey RPC_METHOD_ORIGINAL = + AttributeKey.stringKey("rpc.method_original"); + + private final RpcAttributesGetter getter; - RpcCommonAttributesExtractor(RpcAttributesGetter getter) { + RpcCommonAttributesExtractor(RpcAttributesGetter getter) { this.getter = getter; } + @SuppressWarnings("deprecation") // for getMethod() @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)); + String method = getter.getRpcMethod(request); + if (getter.isPredefined(request)) { + internalSet(attributes, RPC_METHOD, method); + } else { + internalSet(attributes, RPC_METHOD_ORIGINAL, method); + internalSet(attributes, RPC_METHOD, "_OTHER"); + } + } + + if (SemconvStability.emitOldRpcSemconv()) { + internalSet(attributes, RPC_SYSTEM, system); + internalSet(attributes, RPC_SERVICE, getter.getService(request)); + if (!SemconvStability.emitStableRpcSemconv()) { + // only set old rpc.method on spans when there's no clash with stable rpc.method + internalSet(attributes, RPC_METHOD, getter.getMethod(request)); + } + } } @Override @@ -41,6 +93,13 @@ public final void onEnd( REQUEST request, @Nullable RESPONSE response, @Nullable Throwable error) { - // No response attributes + if (SemconvStability.emitStableRpcSemconv()) { + String errorType = getter.getErrorType(request, response, error); + // fall back to exception class name & _OTHER + if (errorType == null && error != null) { + errorType = error.getClass().getName(); + } + internalSet(attributes, ERROR_TYPE, errorType); + } } } 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..773937c128b0 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,108 @@ 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_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); + + // 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_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/RpcServerAttributesExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcServerAttributesExtractor.java index 768af20dc7d6..161c78f466dd 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcServerAttributesExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcServerAttributesExtractor.java @@ -22,11 +22,11 @@ public final class RpcServerAttributesExtractor /** Creates the RPC server attributes extractor. */ public static AttributesExtractor create( - RpcAttributesGetter getter) { + RpcAttributesGetter getter) { return new RpcServerAttributesExtractor<>(getter); } - private RpcServerAttributesExtractor(RpcAttributesGetter getter) { + private RpcServerAttributesExtractor(RpcAttributesGetter getter) { super(getter); } 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..7c87351936eb 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,6 +5,8 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.rpc; +import static io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcCommonAttributesExtractor.OLD_RPC_METHOD_CONTEXT_KEY; +import static io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcCommonAttributesExtractor.RPC_METHOD; import static java.util.logging.Level.FINE; import com.google.auto.value.AutoValue; @@ -19,8 +21,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("Measures the duration of inbound remote procedure calls (RPC).") + .setUnit("s"); + RpcMetricsAdvice.applyServerDurationAdvice(stableDurationBuilder, true); + stableServerDurationHistogram = stableDurationBuilder.build(); + } else { + stableServerDurationHistogram = null; + } LongHistogramBuilder requestSizeBuilder = meter @@ -55,8 +79,8 @@ private RpcServerMetrics(Meter meter) { .setUnit("By") .setDescription("Measures the size of RPC request messages (uncompressed).") .ofLongs(); - RpcMetricsAdvice.applyServerRequestSizeAdvice(requestSizeBuilder); - serverRequestSize = requestSizeBuilder.build(); + RpcMetricsAdvice.applyOldServerRequestSizeAdvice(requestSizeBuilder); + oldServerRequestSize = requestSizeBuilder.build(); LongHistogramBuilder responseSizeBuilder = meter @@ -64,8 +88,8 @@ private RpcServerMetrics(Meter meter) { .setUnit("By") .setDescription("Measures the size of RPC response messages (uncompressed).") .ofLongs(); - RpcMetricsAdvice.applyServerRequestSizeAdvice(responseSizeBuilder); - serverResponseSize = responseSizeBuilder.build(); + RpcMetricsAdvice.applyOldServerRequestSizeAdvice(responseSizeBuilder); + oldServerResponseSize = responseSizeBuilder.build(); } /** @@ -81,7 +105,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 +120,41 @@ 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) { + Attributes oldAttributes = getOldAttributes(attributes, state); + oldServerDurationHistogram.record(durationNanos / NANOS_PER_MS, oldAttributes, 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()) { + Attributes oldAttributes = getOldAttributes(attributes, state); + + Long rpcServerRequestBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_REQUEST_SIZE); + if (rpcServerRequestBodySize != null) { + oldServerRequestSize.record(rpcServerRequestBodySize, oldAttributes, context); + } + + Long rpcServerResponseBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_RESPONSE_SIZE); + if (rpcServerResponseBodySize != null) { + oldServerResponseSize.record(rpcServerResponseBodySize, oldAttributes, 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 +163,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/RpcSizeAttributesExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcSizeAttributesExtractor.java index dd085e411bdd..2e904f1f8240 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcSizeAttributesExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcSizeAttributesExtractor.java @@ -19,9 +19,9 @@ public final class RpcSizeAttributesExtractor static final AttributeKey RPC_REQUEST_SIZE = AttributeKey.longKey("rpc.request.size"); static final AttributeKey RPC_RESPONSE_SIZE = AttributeKey.longKey("rpc.response.size"); - private final RpcAttributesGetter getter; + private final RpcAttributesGetter getter; - RpcSizeAttributesExtractor(RpcAttributesGetter getter) { + RpcSizeAttributesExtractor(RpcAttributesGetter getter) { this.getter = getter; } @@ -30,7 +30,7 @@ public final class RpcSizeAttributesExtractor * attributesGetter} instance to determine the request and response size. */ public static RpcSizeAttributesExtractor create( - RpcAttributesGetter attributesGetter) { + RpcAttributesGetter attributesGetter) { return new RpcSizeAttributesExtractor<>(attributesGetter); } diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcSpanNameExtractor.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcSpanNameExtractor.java index 921839bd9234..fcd07fefd017 100644 --- a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcSpanNameExtractor.java +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcSpanNameExtractor.java @@ -6,6 +6,7 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.rpc; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; /** A {@link SpanNameExtractor} for RPC requests. */ public final class RpcSpanNameExtractor implements SpanNameExtractor { @@ -15,18 +16,31 @@ public final class RpcSpanNameExtractor implements SpanNameExtractor/}. */ public static SpanNameExtractor create( - RpcAttributesGetter attributesExtractor) { + RpcAttributesGetter attributesExtractor) { return new RpcSpanNameExtractor<>(attributesExtractor); } - private final RpcAttributesGetter getter; + private final RpcAttributesGetter getter; - private RpcSpanNameExtractor(RpcAttributesGetter getter) { + private RpcSpanNameExtractor(RpcAttributesGetter getter) { this.getter = getter; } + @SuppressWarnings("deprecation") // for getMethod() @Override public String extract(REQUEST request) { + if (SemconvStability.emitStableRpcSemconv()) { + String method = getter.getRpcMethod(request); + if (method != null) { + return method; + } + String system = getter.getSystem(request); + if (system != null) { + return system; + } + return "RPC request"; + } + String service = getter.getService(request); String method = getter.getMethod(request); if (service == null || method == null) { 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 058bfb3485b3..bd735e18e24e 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 @@ -5,23 +5,34 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.rpc; +import static io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcCommonAttributesExtractor.RPC_METHOD_ORIGINAL; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE; 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 javax.annotation.Nullable; import org.junit.jupiter.api.Test; @SuppressWarnings("deprecation") // using deprecated semconv class RpcAttributesExtractorTest { - enum TestGetter implements RpcAttributesGetter> { - INSTANCE; + private static class TestGetter implements RpcAttributesGetter, Void> { + + private final boolean predefined; + + private TestGetter(boolean predefined) { + this.predefined = predefined; + } @Override public String getSystem(Map request) { @@ -33,41 +44,168 @@ public String getService(Map request) { return request.get("service"); } + @Deprecated @Override public String getMethod(Map request) { return request.get("method"); } + + @Nullable + @Override + public String getRpcMethod(Map request) { + String service = getService(request); + String method = getMethod(request); + if (service == null || method == null) { + return null; + } + return service + "/" + method; + } + + @Nullable + @Override + public String getErrorType( + Map request, @Nullable Void response, @Nullable Throwable error) { + return request.get("errorType"); + } + + @Override + public boolean isPredefined(Map stringStringMap) { + return predefined; + } } @Test void server() { - testExtractor(RpcServerAttributesExtractor.create(TestGetter.INSTANCE)); + testExtractor(RpcServerAttributesExtractor.create(new TestGetter(false)), "my.Service/Method"); + } + + @Test + void serverPredefined() { + testExtractor(RpcServerAttributesExtractor.create(new TestGetter(true)), null); } @Test void client() { - testExtractor(RpcClientAttributesExtractor.create(TestGetter.INSTANCE)); + testExtractor(RpcClientAttributesExtractor.create(new TestGetter(false)), "my.Service/Method"); + } + + @Test + void clientPredefined() { + testExtractor(RpcClientAttributesExtractor.create(new TestGetter(true)), null); + } + + // 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, @Nullable String originalMethod) { + Map request = new HashMap<>(); + request.put("service", "my.Service"); + request.put("method", "Method"); + + Context context = Context.root(); + + AttributesBuilder attributes = Attributes.builder(); + extractor.onStart(attributes, context, request); + + // Build expected entries list based on semconv mode + List, ?>> expectedEntries = new ArrayList<>(); + + if (SemconvStability.emitStableRpcSemconv()) { + expectedEntries.add(entry(RPC_SYSTEM_NAME, "test")); + if (originalMethod != null) { + expectedEntries.add(entry(RPC_METHOD_ORIGINAL, originalMethod)); + expectedEntries.add(entry(RPC_METHOD, "_OTHER")); + } else { + expectedEntries.add(entry(RPC_METHOD, "my.Service/Method")); + } + } + + if (SemconvStability.emitOldRpcSemconv()) { + expectedEntries.add(entry(RPC_SYSTEM, "test")); + expectedEntries.add(entry(RPC_SERVICE, "my.Service")); + if (!SemconvStability.emitStableRpcSemconv()) { + expectedEntries.add(entry(RPC_METHOD, "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(expectedArray); } - private static void testExtractor(AttributesExtractor, Void> extractor) { + @Test + void shouldExtractErrorType_getter() { Map request = new HashMap<>(); request.put("service", "my.Service"); request.put("method", "Method"); + request.put("errorType", "CANCELLED"); + + AttributesExtractor, Void> extractor = + RpcServerAttributesExtractor.create(new TestGetter(false)); Context context = Context.root(); + AttributesBuilder attributes = Attributes.builder(); + extractor.onStart(attributes, context, request); + extractor.onEnd(attributes, context, request, null, null); + + if (SemconvStability.emitStableRpcSemconv()) { + assertThat(attributes.build()).containsEntry(ERROR_TYPE, "CANCELLED"); + } + } + + @Test + void shouldExtractErrorType_exceptionClassName() { + Map request = new HashMap<>(); + request.put("service", "my.Service"); + request.put("method", "Method"); + AttributesExtractor, Void> extractor = + RpcServerAttributesExtractor.create(new TestGetter(false)); + + Context context = Context.root(); + AttributesBuilder attributes = Attributes.builder(); + extractor.onStart(attributes, context, request); + extractor.onEnd(attributes, context, request, null, new IllegalArgumentException()); + + if (SemconvStability.emitStableRpcSemconv()) { + assertThat(attributes.build()) + .containsEntry(ERROR_TYPE, "java.lang.IllegalArgumentException"); + } + } + + @Test + void shouldNotExtractErrorType_noError() { + Map request = new HashMap<>(); + request.put("service", "my.Service"); + request.put("method", "Method"); + + AttributesExtractor, Void> extractor = + RpcServerAttributesExtractor.create(new TestGetter(false)); + + Context context = Context.root(); 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")); 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")); + + if (SemconvStability.emitStableRpcSemconv()) { + assertThat(attributes.build()).doesNotContainKey(ERROR_TYPE); + } } } 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 8a0dbc5a32c7..897bd24e9341 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,33 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.rpc; +import static io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcCommonAttributesExtractor.OLD_RPC_METHOD_CONTEXT_KEY; +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; @@ -35,19 +47,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() @@ -73,130 +76,256 @@ void collectsMetrics() { "090a0b0c0d0e0f00", TraceFlags.getSampled(), TraceState.getDefault()))); + if (SemconvStability.emitOldRpcSemconv() && SemconvStability.emitStableRpcSemconv()) { + parent = parent.with(OLD_RPC_METHOD_CONTEXT_KEY, "exampleMethod"); + } Context context1 = listener.onStart(parent, requestAttributes1, nanos(100)); assertThat(metricReader.collectAllMetrics()).isEmpty(); - Context context2 = listener.onStart(Context.root(), requestAttributes2, nanos(150)); + Context context2Root = Context.root(); + if (SemconvStability.emitOldRpcSemconv() && SemconvStability.emitStableRpcSemconv()) { + context2Root = context2Root.with(OLD_RPC_METHOD_CONTEXT_KEY, "exampleMethod"); + } + Context context2 = listener.onStart(context2Root, 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(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); + if (!SemconvStability.emitStableRpcSemconv()) { + builder.put(RPC_METHOD, 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 d7063861f031..1863c7873245 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,34 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.rpc; +import static io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcCommonAttributesExtractor.OLD_RPC_METHOD_CONTEXT_KEY; +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; @@ -35,19 +48,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() @@ -75,130 +79,258 @@ void collectsMetrics() { "090a0b0c0d0e0f00", TraceFlags.getSampled(), TraceState.getDefault()))); + if (SemconvStability.emitOldRpcSemconv() && SemconvStability.emitStableRpcSemconv()) { + parent = parent.with(OLD_RPC_METHOD_CONTEXT_KEY, "exampleMethod"); + } Context context1 = listener.onStart(parent, requestAttributes1, nanos(100)); assertThat(metricReader.collectAllMetrics()).isEmpty(); - Context context2 = listener.onStart(Context.root(), requestAttributes2, nanos(150)); + Context context2Root = Context.root(); + if (SemconvStability.emitOldRpcSemconv() && SemconvStability.emitStableRpcSemconv()) { + context2Root = context2Root.with(OLD_RPC_METHOD_CONTEXT_KEY, "exampleMethod"); + } + Context context2 = listener.onStart(context2Root, 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(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); + if (!SemconvStability.emitStableRpcSemconv()) { + builder.put(RPC_METHOD, 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/RpcSpanNameExtractorTest.java b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcSpanNameExtractorTest.java index e20af5c494a4..503367ed0add 100644 --- a/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcSpanNameExtractorTest.java +++ b/instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcSpanNameExtractorTest.java @@ -6,9 +6,11 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.rpc; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.mockito.Mockito.when; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -17,21 +19,29 @@ @ExtendWith(MockitoExtension.class) class RpcSpanNameExtractorTest { - @Mock RpcAttributesGetter getter; + @Mock RpcAttributesGetter getter; @Test + @SuppressWarnings("deprecation") // testing deprecated method void normal() { RpcRequest request = new RpcRequest(); - when(getter.getService(request)).thenReturn("my.Service"); - when(getter.getMethod(request)).thenReturn("Method"); + if (SemconvStability.emitStableRpcSemconv()) { + when(getter.getRpcMethod(request)).thenReturn("my.Service/Method"); + } else { + when(getter.getService(request)).thenReturn("my.Service"); + when(getter.getMethod(request)).thenReturn("Method"); + } SpanNameExtractor extractor = RpcSpanNameExtractor.create(getter); assertThat(extractor.extract(request)).isEqualTo("my.Service/Method"); } @Test + @SuppressWarnings("deprecation") // testing deprecated method void serviceNull() { + assumeTrue(!SemconvStability.emitStableRpcSemconv()); + RpcRequest request = new RpcRequest(); when(getter.getMethod(request)).thenReturn("Method"); @@ -42,6 +52,8 @@ void serviceNull() { @Test void methodNull() { + assumeTrue(!SemconvStability.emitStableRpcSemconv()); + RpcRequest request = new RpcRequest(); when(getter.getService(request)).thenReturn("my.Service"); @@ -50,5 +62,27 @@ void methodNull() { assertThat(extractor.extract(request)).isEqualTo("RPC request"); } + @Test + void rpcMethodSystemFallback() { + assumeTrue(SemconvStability.emitStableRpcSemconv()); + + RpcRequest request = new RpcRequest(); + + when(getter.getSystem(request)).thenReturn("system"); + + SpanNameExtractor extractor = RpcSpanNameExtractor.create(getter); + assertThat(extractor.extract(request)).isEqualTo("system"); + } + + @Test + void rpcMethodNull() { + assumeTrue(SemconvStability.emitStableRpcSemconv()); + + RpcRequest request = new RpcRequest(); + + SpanNameExtractor extractor = RpcSpanNameExtractor.create(getter); + assertThat(extractor.extract(request)).isEqualTo("RPC request"); + } + static class RpcRequest {} } 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 94963ef35190..6547efca8950 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 @@ -27,6 +27,9 @@ public final class SemconvStability { private static final boolean emitOldServicePeerSemconv; private static final boolean emitStableServicePeerSemconv; + private static final boolean emitOldRpcSemconv; + private static final boolean emitStableRpcSemconv; + static { boolean oldDatabase = true; boolean stableDatabase = false; @@ -37,6 +40,9 @@ public final class SemconvStability { boolean oldServicePeer = true; boolean stableServicePeer = false; + boolean oldRpc = true; + boolean stableRpc = false; + String value = System.getProperty("otel.semconv-stability.opt-in"); if (value == null) { value = System.getenv("OTEL_SEMCONV_STABILITY_OPT_IN"); @@ -73,6 +79,15 @@ public final class SemconvStability { oldServicePeer = true; stableServicePeer = true; } + + if (values.contains("rpc")) { + oldRpc = false; + stableRpc = true; + } + if (values.contains("rpc/dup")) { + oldRpc = true; + stableRpc = true; + } } emitOldDatabaseSemconv = oldDatabase; @@ -83,6 +98,9 @@ public final class SemconvStability { emitOldServicePeerSemconv = oldServicePeer; emitStableServicePeerSemconv = stableServicePeer; + + emitOldRpcSemconv = oldRpc; + emitStableRpcSemconv = stableRpc; } public static boolean emitOldDatabaseSemconv() { @@ -134,5 +152,25 @@ 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; + } + private SemconvStability() {} } diff --git a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboRpcAttributesGetter.java b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboRpcAttributesGetter.java index aac66a138c01..e2683d206c71 100644 --- a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboRpcAttributesGetter.java +++ b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboRpcAttributesGetter.java @@ -6,8 +6,9 @@ package io.opentelemetry.instrumentation.apachedubbo.v2_7; import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcAttributesGetter; +import org.apache.dubbo.rpc.Result; -enum DubboRpcAttributesGetter implements RpcAttributesGetter { +enum DubboRpcAttributesGetter implements RpcAttributesGetter { INSTANCE; @Override @@ -20,6 +21,7 @@ public String getService(DubboRequest request) { return request.invocation().getInvoker().getInterface().getName(); } + @Deprecated @Override public String getMethod(DubboRequest request) { return request.invocation().getMethodName(); diff --git a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTelemetryBuilder.java b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTelemetryBuilder.java index a0645eb5e21e..92020fc47700 100644 --- a/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTelemetryBuilder.java +++ b/instrumentation/apache-dubbo-2.7/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/apachedubbo/v2_7/DubboTelemetryBuilder.java @@ -11,6 +11,7 @@ import io.opentelemetry.instrumentation.apachedubbo.v2_7.internal.DubboClientNetworkAttributesGetter; import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcClientAttributesExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcClientMetrics; +import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcCommonAttributesExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcServerAttributesExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcServerMetrics; import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcSpanNameExtractor; @@ -116,7 +117,9 @@ public DubboTelemetry build() { .addAttributesExtractor(RpcServerAttributesExtractor.create(rpcAttributesGetter)) .addAttributesExtractor(NetworkAttributesExtractor.create(netServerAttributesGetter)) .addAttributesExtractors(attributesExtractors) - .addOperationMetrics(RpcServerMetrics.get()); + .addOperationMetrics(RpcServerMetrics.get()) + .addContextCustomizer( + RpcCommonAttributesExtractor.oldMethodContextCustomizer(rpcAttributesGetter)); InstrumenterBuilder clientInstrumenterBuilder = Instrumenter.builder( @@ -125,7 +128,9 @@ public DubboTelemetry build() { .addAttributesExtractor(ServerAttributesExtractor.create(netClientAttributesGetter)) .addAttributesExtractor(NetworkAttributesExtractor.create(netClientAttributesGetter)) .addAttributesExtractors(attributesExtractors) - .addOperationMetrics(RpcClientMetrics.get()); + .addOperationMetrics(RpcClientMetrics.get()) + .addContextCustomizer( + RpcCommonAttributesExtractor.oldMethodContextCustomizer(rpcAttributesGetter)); if (peerService != null) { clientInstrumenterBuilder.addAttributesExtractor( diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkRpcAttributesGetter.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkRpcAttributesGetter.java index a3704e88855b..3d014d0e4a48 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkRpcAttributesGetter.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkRpcAttributesGetter.java @@ -6,9 +6,10 @@ package io.opentelemetry.instrumentation.awssdk.v1_11; import com.amazonaws.Request; +import com.amazonaws.Response; import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcAttributesGetter; -enum AwsSdkRpcAttributesGetter implements RpcAttributesGetter> { +enum AwsSdkRpcAttributesGetter implements RpcAttributesGetter, Response> { INSTANCE; private static final ClassValue OPERATION_NAME = @@ -37,6 +38,7 @@ public String getService(Request request) { return request.getServiceName(); } + @Deprecated @Override public String getMethod(Request request) { return OPERATION_NAME.get(request.getOriginalRequest().getClass()); diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkSpanNameExtractor.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkSpanNameExtractor.java index f757c4c026e3..cd5954f80609 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkSpanNameExtractor.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkSpanNameExtractor.java @@ -14,6 +14,7 @@ class AwsSdkSpanNameExtractor implements SpanNameExtractor> { private static final AwsSdkRpcAttributesGetter rpcAttributes = AwsSdkRpcAttributesGetter.INSTANCE; private final NamesCache namesCache = new NamesCache(); + @SuppressWarnings("deprecation") // for getMethod() @Override public String extract(Request request) { return qualifiedOperation( diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/AwsSdkRpcAttributesGetter.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/AwsSdkRpcAttributesGetter.java index f6a6ba078a2b..10e5f7fa4115 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/AwsSdkRpcAttributesGetter.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/AwsSdkRpcAttributesGetter.java @@ -9,7 +9,7 @@ import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; -enum AwsSdkRpcAttributesGetter implements RpcAttributesGetter { +enum AwsSdkRpcAttributesGetter implements RpcAttributesGetter { INSTANCE; @Override @@ -22,6 +22,7 @@ public String getService(ExecutionAttributes request) { return request.getAttribute(SdkExecutionAttribute.SERVICE_NAME); } + @Deprecated @Override public String getMethod(ExecutionAttributes request) { return request.getAttribute(SdkExecutionAttribute.OPERATION_NAME); 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..a7211fd13fef 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 @@ -6,6 +6,7 @@ package io.opentelemetry.instrumentation.grpc.v1_6; import io.grpc.Metadata; +import io.grpc.Status; import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcAttributesGetter; import java.util.Collections; import java.util.List; @@ -13,7 +14,7 @@ import java.util.stream.StreamSupport; import javax.annotation.Nullable; -enum GrpcRpcAttributesGetter implements RpcAttributesGetter { +enum GrpcRpcAttributesGetter implements RpcAttributesGetter { INSTANCE; @Override @@ -32,6 +33,7 @@ public String getService(GrpcRequest request) { return fullMethodName.substring(0, slashIndex); } + @Deprecated @Override @Nullable public String getMethod(GrpcRequest request) { diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTelemetryBuilder.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTelemetryBuilder.java index 041cfdbbdc90..91c7ff45f67f 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTelemetryBuilder.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcTelemetryBuilder.java @@ -11,6 +11,7 @@ import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcClientAttributesExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcClientMetrics; +import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcCommonAttributesExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcServerAttributesExtractor; import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcServerMetrics; import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcSizeAttributesExtractor; @@ -202,6 +203,8 @@ public GrpcTelemetry build() { .addOperationMetrics(RpcClientMetrics.get()); Experimental.addOperationListenerAttributesExtractor( clientInstrumenterBuilder, RpcSizeAttributesExtractor.create(rpcAttributesGetter)); + clientInstrumenterBuilder.addContextCustomizer( + RpcCommonAttributesExtractor.oldMethodContextCustomizer(rpcAttributesGetter)); serverInstrumenterBuilder .setSpanStatusExtractor(GrpcSpanStatusExtractor.SERVER) .addAttributesExtractors(additionalExtractors) @@ -215,6 +218,8 @@ public GrpcTelemetry build() { .addOperationMetrics(RpcServerMetrics.get()); Experimental.addOperationListenerAttributesExtractor( serverInstrumenterBuilder, RpcSizeAttributesExtractor.create(rpcAttributesGetter)); + serverInstrumenterBuilder.addContextCustomizer( + RpcCommonAttributesExtractor.oldMethodContextCustomizer(rpcAttributesGetter)); if (peerService != null) { clientInstrumenterBuilder.addAttributesExtractor( diff --git a/instrumentation/gwt-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtRpcAttributesGetter.java b/instrumentation/gwt-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtRpcAttributesGetter.java index 3ebef129f191..1dd84824e59c 100644 --- a/instrumentation/gwt-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtRpcAttributesGetter.java +++ b/instrumentation/gwt-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtRpcAttributesGetter.java @@ -8,7 +8,7 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcAttributesGetter; import java.lang.reflect.Method; -enum GwtRpcAttributesGetter implements RpcAttributesGetter { +enum GwtRpcAttributesGetter implements RpcAttributesGetter { INSTANCE; @Override @@ -21,6 +21,7 @@ public String getService(Method method) { return method.getDeclaringClass().getName(); } + @Deprecated @Override public String getMethod(Method method) { return method.getName(); diff --git a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/client/RmiClientAttributesGetter.java b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/client/RmiClientAttributesGetter.java index 1b139dacc7aa..95c01d2d7275 100644 --- a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/client/RmiClientAttributesGetter.java +++ b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/client/RmiClientAttributesGetter.java @@ -8,7 +8,7 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcAttributesGetter; import java.lang.reflect.Method; -enum RmiClientAttributesGetter implements RpcAttributesGetter { +enum RmiClientAttributesGetter implements RpcAttributesGetter { INSTANCE; @Override @@ -21,6 +21,7 @@ public String getService(Method method) { return method.getDeclaringClass().getName(); } + @Deprecated @Override public String getMethod(Method method) { return method.getName(); diff --git a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/server/RmiServerAttributesGetter.java b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/server/RmiServerAttributesGetter.java index ad598bb53814..f8947eba3df5 100644 --- a/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/server/RmiServerAttributesGetter.java +++ b/instrumentation/rmi/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rmi/server/RmiServerAttributesGetter.java @@ -8,7 +8,7 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcAttributesGetter; import io.opentelemetry.instrumentation.api.incubator.semconv.util.ClassAndMethod; -enum RmiServerAttributesGetter implements RpcAttributesGetter { +enum RmiServerAttributesGetter implements RpcAttributesGetter { INSTANCE; @Override @@ -21,6 +21,7 @@ public String getService(ClassAndMethod classAndMethod) { return classAndMethod.declaringClass().getName(); } + @Deprecated @Override public String getMethod(ClassAndMethod classAndMethod) { return classAndMethod.methodName(); diff --git a/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rmi/v4_0/client/ClientAttributesGetter.java b/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rmi/v4_0/client/ClientAttributesGetter.java index 19d31b217770..9ed8b87924a0 100644 --- a/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rmi/v4_0/client/ClientAttributesGetter.java +++ b/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rmi/v4_0/client/ClientAttributesGetter.java @@ -8,7 +8,7 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcAttributesGetter; import java.lang.reflect.Method; -public enum ClientAttributesGetter implements RpcAttributesGetter { +public enum ClientAttributesGetter implements RpcAttributesGetter { INSTANCE; @Override @@ -21,6 +21,7 @@ public String getService(Method method) { return method.getDeclaringClass().getName(); } + @Deprecated @Override public String getMethod(Method method) { return method.getName(); diff --git a/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rmi/v4_0/server/ServerAttributesGetter.java b/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rmi/v4_0/server/ServerAttributesGetter.java index e7bf12be54cc..b4c150ca1dfe 100644 --- a/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rmi/v4_0/server/ServerAttributesGetter.java +++ b/instrumentation/spring/spring-rmi-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/rmi/v4_0/server/ServerAttributesGetter.java @@ -8,7 +8,7 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcAttributesGetter; import io.opentelemetry.instrumentation.api.incubator.semconv.util.ClassAndMethod; -public enum ServerAttributesGetter implements RpcAttributesGetter { +public enum ServerAttributesGetter implements RpcAttributesGetter { INSTANCE; @Override @@ -21,6 +21,7 @@ public String getService(ClassAndMethod classAndMethod) { return classAndMethod.declaringClass().getName(); } + @Deprecated @Override public String getMethod(ClassAndMethod classAndMethod) { return classAndMethod.methodName();