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 2e7d0a6e539d..0da544340f6b 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 @@ -24,4 +24,14 @@ public interface RpcAttributesGetter { @Nullable String getMethod(REQUEST request); + + @Nullable + default Long getRequestSize(REQUEST request) { + return null; + } + + @Nullable + default Long getResponseSize(REQUEST request) { + return null; + } } 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 e0e379410f4f..0135bd20f799 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 @@ -11,6 +11,8 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.DoubleHistogramBuilder; +import io.opentelemetry.api.metrics.LongHistogram; +import io.opentelemetry.api.metrics.LongHistogramBuilder; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.context.Context; import io.opentelemetry.context.ContextKey; @@ -35,6 +37,8 @@ public final class RpcClientMetrics implements OperationListener { private static final Logger logger = Logger.getLogger(RpcClientMetrics.class.getName()); private final DoubleHistogram clientDurationHistogram; + private final LongHistogram clientRequestSize; + private final LongHistogram clientResponseSize; private RpcClientMetrics(Meter meter) { DoubleHistogramBuilder durationBuilder = @@ -44,6 +48,24 @@ private RpcClientMetrics(Meter meter) { .setUnit("ms"); RpcMetricsAdvice.applyClientDurationAdvice(durationBuilder); clientDurationHistogram = durationBuilder.build(); + + LongHistogramBuilder requestSizeBuilder = + meter + .histogramBuilder("rpc.client.request.size") + .setUnit("By") + .setDescription("Measures the size of RPC request messages (uncompressed).") + .ofLongs(); + RpcMetricsAdvice.applyClientRequestSizeAdvice(requestSizeBuilder); + clientRequestSize = requestSizeBuilder.build(); + + LongHistogramBuilder responseSizeBuilder = + meter + .histogramBuilder("rpc.client.response.size") + .setUnit("By") + .setDescription("Measures the size of RPC response messages (uncompressed).") + .ofLongs(); + RpcMetricsAdvice.applyClientRequestSizeAdvice(responseSizeBuilder); + clientResponseSize = responseSizeBuilder.build(); } /** @@ -72,10 +94,19 @@ public void onEnd(Context context, Attributes endAttributes, long endNanos) { context); return; } + Attributes attributes = state.startAttributes().toBuilder().putAll(endAttributes).build(); clientDurationHistogram.record( - (endNanos - state.startTimeNanos()) / NANOS_PER_MS, - state.startAttributes().toBuilder().putAll(endAttributes).build(), - context); + (endNanos - state.startTimeNanos()) / NANOS_PER_MS, attributes, context); + + Long rpcClientRequestBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_REQUEST_SIZE); + if (rpcClientRequestBodySize != null) { + clientRequestSize.record(rpcClientRequestBodySize, attributes, context); + } + + Long rpcClientResponseBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_RESPONSE_SIZE); + if (rpcClientResponseBodySize != null) { + clientResponseSize.record(rpcClientResponseBodySize, attributes, context); + } } @AutoValue 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 0a24cca3f39c..af317d876d62 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,18 +5,32 @@ 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.semconv.NetworkAttributes; import io.opentelemetry.semconv.ServerAttributes; -import java.util.Arrays; +import java.util.List; final class RpcMetricsAdvice { // copied from RpcIncubatingAttributes 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) { if (!(builder instanceof ExtendedDoubleHistogramBuilder)) { @@ -24,17 +38,7 @@ static void applyClientDurationAdvice(DoubleHistogramBuilder builder) { } // 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( - Arrays.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)); + ((ExtendedDoubleHistogramBuilder) builder).setAttributesAdvice(RPC_METRICS_ATTRIBUTE_KEYS); } static void applyServerDurationAdvice(DoubleHistogramBuilder builder) { @@ -43,17 +47,25 @@ static void applyServerDurationAdvice(DoubleHistogramBuilder builder) { } // 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( - Arrays.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)); + ((ExtendedDoubleHistogramBuilder) builder).setAttributesAdvice(RPC_METRICS_ATTRIBUTE_KEYS); + } + + static void applyClientRequestSizeAdvice(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); + } + + static void applyServerRequestSizeAdvice(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); } private RpcMetricsAdvice() {} diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcServerMetrics.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcServerMetrics.java index 526db167bdfd..2ab6755853d2 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 @@ -11,6 +11,8 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.DoubleHistogramBuilder; +import io.opentelemetry.api.metrics.LongHistogram; +import io.opentelemetry.api.metrics.LongHistogramBuilder; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.context.Context; import io.opentelemetry.context.ContextKey; @@ -35,6 +37,8 @@ public final class RpcServerMetrics implements OperationListener { private static final Logger logger = Logger.getLogger(RpcServerMetrics.class.getName()); private final DoubleHistogram serverDurationHistogram; + private final LongHistogram serverRequestSize; + private final LongHistogram serverResponseSize; private RpcServerMetrics(Meter meter) { DoubleHistogramBuilder durationBuilder = @@ -44,6 +48,24 @@ private RpcServerMetrics(Meter meter) { .setUnit("ms"); RpcMetricsAdvice.applyServerDurationAdvice(durationBuilder); serverDurationHistogram = durationBuilder.build(); + + LongHistogramBuilder requestSizeBuilder = + meter + .histogramBuilder("rpc.server.request.size") + .setUnit("By") + .setDescription("Measures the size of RPC request messages (uncompressed).") + .ofLongs(); + RpcMetricsAdvice.applyServerRequestSizeAdvice(requestSizeBuilder); + serverRequestSize = requestSizeBuilder.build(); + + LongHistogramBuilder responseSizeBuilder = + meter + .histogramBuilder("rpc.server.response.size") + .setUnit("By") + .setDescription("Measures the size of RPC response messages (uncompressed).") + .ofLongs(); + RpcMetricsAdvice.applyServerRequestSizeAdvice(responseSizeBuilder); + serverResponseSize = responseSizeBuilder.build(); } /** @@ -72,10 +94,19 @@ public void onEnd(Context context, Attributes endAttributes, long endNanos) { context); return; } + Attributes attributes = state.startAttributes().toBuilder().putAll(endAttributes).build(); serverDurationHistogram.record( - (endNanos - state.startTimeNanos()) / NANOS_PER_MS, - state.startAttributes().toBuilder().putAll(endAttributes).build(), - context); + (endNanos - state.startTimeNanos()) / NANOS_PER_MS, attributes, context); + + Long rpcServerRequestBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_REQUEST_SIZE); + if (rpcServerRequestBodySize != null) { + serverRequestSize.record(rpcServerRequestBodySize, attributes, context); + } + + Long rpcServerResponseBodySize = attributes.get(RpcSizeAttributesExtractor.RPC_RESPONSE_SIZE); + if (rpcServerResponseBodySize != null) { + serverResponseSize.record(rpcServerResponseBodySize, attributes, context); + } } @AutoValue 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 new file mode 100644 index 000000000000..dd085e411bdd --- /dev/null +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcSizeAttributesExtractor.java @@ -0,0 +1,50 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.incubator.semconv.rpc; + +import static io.opentelemetry.instrumentation.api.internal.AttributesExtractorUtil.internalSet; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import javax.annotation.Nullable; + +public final class RpcSizeAttributesExtractor + implements AttributesExtractor { + + 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; + + RpcSizeAttributesExtractor(RpcAttributesGetter getter) { + this.getter = getter; + } + + /** + * Returns a new {@link RpcSizeAttributesExtractor} that will use the passed {@code + * attributesGetter} instance to determine the request and response size. + */ + public static RpcSizeAttributesExtractor create( + RpcAttributesGetter attributesGetter) { + return new RpcSizeAttributesExtractor<>(attributesGetter); + } + + @Override + public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST request) {} + + @Override + public void onEnd( + AttributesBuilder attributes, + Context context, + REQUEST request, + @Nullable RESPONSE response, + @Nullable Throwable error) { + internalSet(attributes, RPC_REQUEST_SIZE, getter.getRequestSize(request)); + internalSet(attributes, RPC_RESPONSE_SIZE, getter.getResponseSize(request)); + } +} 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 768a67de0bb2..ec37a9bb4439 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 @@ -33,7 +33,15 @@ void collectsMetrics() { OperationListener listener = RpcClientMetrics.get().create(meterProvider.get("test")); - Attributes requestAttributes = + 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(); + + Attributes requestAttributes2 = Attributes.builder() .put(RpcIncubatingAttributes.RPC_SYSTEM, "grpc") .put(RpcIncubatingAttributes.RPC_SERVICE, "myservice.EchoService") @@ -46,6 +54,7 @@ void collectsMetrics() { .put(ServerAttributes.SERVER_PORT, 8080) .put(NetworkAttributes.NETWORK_TRANSPORT, "tcp") .put(NetworkAttributes.NETWORK_TYPE, "ipv4") + .put(RpcSizeAttributesExtractor.RPC_RESPONSE_SIZE, 20) .build(); Attributes responseAttributes2 = @@ -64,11 +73,11 @@ void collectsMetrics() { TraceFlags.getSampled(), TraceState.getDefault()))); - Context context1 = listener.onStart(parent, requestAttributes, nanos(100)); + Context context1 = listener.onStart(parent, requestAttributes1, nanos(100)); assertThat(metricReader.collectAllMetrics()).isEmpty(); - Context context2 = listener.onStart(Context.root(), requestAttributes, nanos(150)); + Context context2 = listener.onStart(Context.root(), requestAttributes2, nanos(150)); assertThat(metricReader.collectAllMetrics()).isEmpty(); @@ -76,6 +85,62 @@ void collectsMetrics() { 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") @@ -86,7 +151,7 @@ void collectsMetrics() { point -> point .hasSum(150 /* millis */) - .hasAttributesSatisfying( + .hasAttributesSatisfyingExactly( equalTo(RpcIncubatingAttributes.RPC_SYSTEM, "grpc"), equalTo( RpcIncubatingAttributes.RPC_SERVICE, @@ -118,7 +183,7 @@ void collectsMetrics() { point -> point .hasSum(150 /* millis */) - .hasAttributesSatisfying( + .hasAttributesSatisfyingExactly( equalTo(RpcIncubatingAttributes.RPC_SYSTEM, "grpc"), equalTo( RpcIncubatingAttributes.RPC_SERVICE, 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 bb8b59082aa5..e0e622adb2ac 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 @@ -33,7 +33,15 @@ void collectsMetrics() { OperationListener listener = RpcServerMetrics.get().create(meterProvider.get("test")); - Attributes requestAttributes = + 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(); + + Attributes requestAttributes2 = Attributes.builder() .put(RpcIncubatingAttributes.RPC_SYSTEM, "grpc") .put(RpcIncubatingAttributes.RPC_SERVICE, "myservice.EchoService") @@ -47,6 +55,7 @@ void collectsMetrics() { .put(NetworkAttributes.NETWORK_LOCAL_ADDRESS, "127.0.0.1") .put(NetworkAttributes.NETWORK_TRANSPORT, "tcp") .put(NetworkAttributes.NETWORK_TYPE, "ipv4") + .put(RpcSizeAttributesExtractor.RPC_RESPONSE_SIZE, 20) .build(); Attributes responseAttributes2 = @@ -66,11 +75,11 @@ void collectsMetrics() { TraceFlags.getSampled(), TraceState.getDefault()))); - Context context1 = listener.onStart(parent, requestAttributes, nanos(100)); + Context context1 = listener.onStart(parent, requestAttributes1, nanos(100)); assertThat(metricReader.collectAllMetrics()).isEmpty(); - Context context2 = listener.onStart(Context.root(), requestAttributes, nanos(150)); + Context context2 = listener.onStart(Context.root(), requestAttributes2, nanos(150)); assertThat(metricReader.collectAllMetrics()).isEmpty(); @@ -88,7 +97,63 @@ void collectsMetrics() { point -> point .hasSum(150 /* millis */) - .hasAttributesSatisfying( + .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, @@ -97,6 +162,7 @@ void collectsMetrics() { 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( @@ -119,7 +185,7 @@ void collectsMetrics() { point -> point .hasSum(150 /* millis */) - .hasAttributesSatisfying( + .hasAttributesSatisfyingExactly( equalTo(RpcIncubatingAttributes.RPC_SYSTEM, "grpc"), equalTo( RpcIncubatingAttributes.RPC_SERVICE, @@ -127,6 +193,7 @@ void collectsMetrics() { equalTo( RpcIncubatingAttributes.RPC_METHOD, "exampleMethod"), + equalTo(ServerAttributes.SERVER_PORT, 8080), equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp"))))); } diff --git a/instrumentation/armeria/armeria-grpc-1.14/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/armeria/grpc/v1_14/ArmeriaGrpcTest.java b/instrumentation/armeria/armeria-grpc-1.14/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/armeria/grpc/v1_14/ArmeriaGrpcTest.java index 188dcbcc492a..e052719daf25 100644 --- a/instrumentation/armeria/armeria-grpc-1.14/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/armeria/grpc/v1_14/ArmeriaGrpcTest.java +++ b/instrumentation/armeria/armeria-grpc-1.14/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/armeria/grpc/v1_14/ArmeriaGrpcTest.java @@ -63,10 +63,8 @@ void grpcInstrumentation() { GreeterGrpc.GreeterBlockingStub client = GrpcClients.builder(server.httpUri()).build(GreeterGrpc.GreeterBlockingStub.class); - Helloworld.Response response = - testing.runWithSpan( - "parent", - () -> client.sayHello(Helloworld.Request.newBuilder().setName("test").build())); + Helloworld.Request request = Helloworld.Request.newBuilder().setName("test").build(); + Helloworld.Response response = testing.runWithSpan("parent", () -> client.sayHello(request)); assertThat(response.getMessage()).isEqualTo("Hello test"); diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/BodySizeUtil.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/BodySizeUtil.java new file mode 100644 index 000000000000..38b3a3095d35 --- /dev/null +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/BodySizeUtil.java @@ -0,0 +1,49 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.grpc.v1_6; + +import java.lang.reflect.Method; +import javax.annotation.Nullable; + +final class BodySizeUtil { + @Nullable private static final Class messageLiteClass = getMessageLiteClass(); + + @Nullable + private static final Method serializedSizeMethod = + messageLiteClass != null ? getSerializedSizeMethod(messageLiteClass) : null; + + private static Class getMessageLiteClass() { + try { + return Class.forName("com.google.protobuf.MessageLite"); + } catch (Exception ignore) { + return null; + } + } + + private static Method getSerializedSizeMethod(Class clazz) { + try { + return clazz.getMethod("getSerializedSize"); + } catch (NoSuchMethodException ignore) { + return null; + } + } + + static Long getBodySize(T message) { + if (messageLiteClass == null || serializedSizeMethod == null) { + return null; + } + if (!messageLiteClass.isInstance(message)) { + return null; + } + try { + return ((Integer) serializedSizeMethod.invoke(message)).longValue(); + } catch (Throwable ignore) { + return null; + } + } + + private BodySizeUtil() {} +} diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcRequest.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcRequest.java index 89f6746e1ceb..926799191bb2 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcRequest.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/GrpcRequest.java @@ -20,6 +20,9 @@ public final class GrpcRequest { private volatile int logicalPort = -1; @Nullable private volatile SocketAddress peerSocketAddress; + private Long requestSize; + private Long responseSize; + GrpcRequest( MethodDescriptor method, @Nullable Metadata metadata, @@ -78,4 +81,20 @@ public SocketAddress getPeerSocketAddress() { void setPeerSocketAddress(SocketAddress peerSocketAddress) { this.peerSocketAddress = peerSocketAddress; } + + public Long getRequestSize() { + return requestSize; + } + + public void setRequestSize(Long requestSize) { + this.requestSize = requestSize; + } + + public Long getResponseSize() { + return responseSize; + } + + public void setResponseSize(Long responseSize) { + this.responseSize = responseSize; + } } 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 ee7214bb1a87..e38b95c40f85 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 @@ -43,6 +43,18 @@ public String getMethod(GrpcRequest request) { return fullMethodName.substring(slashIndex + 1); } + @Override + @Nullable + public Long getRequestSize(GrpcRequest request) { + return request.getRequestSize(); + } + + @Override + @Nullable + public Long getResponseSize(GrpcRequest request) { + return request.getResponseSize(); + } + List metadataValue(GrpcRequest request, String key) { if (request.getMetadata() == null) { return Collections.emptyList(); 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 ccbd82e56bf9..62addaafa146 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 @@ -13,11 +13,13 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcClientMetrics; 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; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder; import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; +import io.opentelemetry.instrumentation.api.internal.Experimental; import io.opentelemetry.instrumentation.api.semconv.network.NetworkAttributesExtractor; import io.opentelemetry.instrumentation.api.semconv.network.ServerAttributesExtractor; import io.opentelemetry.instrumentation.grpc.v1_6.internal.GrpcClientNetworkAttributesGetter; @@ -185,6 +187,8 @@ public GrpcTelemetry build() { new GrpcAttributesExtractor( GrpcRpcAttributesGetter.INSTANCE, capturedClientRequestMetadata)) .addOperationMetrics(RpcClientMetrics.get()); + Experimental.addOperationListenerAttributesExtractor( + clientInstrumenterBuilder, RpcSizeAttributesExtractor.create(rpcAttributesGetter)); serverInstrumenterBuilder .setSpanStatusExtractor(GrpcSpanStatusExtractor.SERVER) .addAttributesExtractors(additionalExtractors) @@ -196,6 +200,8 @@ public GrpcTelemetry build() { GrpcRpcAttributesGetter.INSTANCE, capturedServerRequestMetadata)) .addAttributesExtractors(additionalServerExtractors) .addOperationMetrics(RpcServerMetrics.get()); + Experimental.addOperationListenerAttributesExtractor( + serverInstrumenterBuilder, RpcSizeAttributesExtractor.create(rpcAttributesGetter)); if (peerService != null) { clientInstrumenterBuilder.addAttributesExtractor( diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingClientInterceptor.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingClientInterceptor.java index 7e89b193b54a..b2a0deb22c62 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingClientInterceptor.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingClientInterceptor.java @@ -126,6 +126,7 @@ public void start(Listener responseListener, Metadata headers) { @Override public void sendMessage(REQUEST message) { + request.setRequestSize(BodySizeUtil.getBodySize(message)); try (Scope ignored = context.makeCurrent()) { super.sendMessage(message); } catch (Throwable e) { @@ -159,6 +160,7 @@ final class TracingClientCallListener @Override public void onMessage(RESPONSE message) { + request.setResponseSize(BodySizeUtil.getBodySize(message)); long messageId = RECEIVED_MESSAGE_ID_UPDATER.incrementAndGet(TracingClientCall.this); if (emitMessageEvents) { Attributes attributes = Attributes.of(MESSAGE_TYPE, RECEIVED, MESSAGE_ID, messageId); diff --git a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingServerInterceptor.java b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingServerInterceptor.java index d52c332ec50a..688099084146 100644 --- a/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingServerInterceptor.java +++ b/instrumentation/grpc-1.6/library/src/main/java/io/opentelemetry/instrumentation/grpc/v1_6/TracingServerInterceptor.java @@ -119,6 +119,7 @@ TracingServerCallListener start(Metadata headers, ServerCallHandler client.sayHello(Helloworld.Request.newBuilder().setName(paramName).build())); - assertThat(response.getMessage()).isEqualTo("Hello " + paramName); + String prefix = "Hello "; + assertThat(response.getMessage()).isEqualTo(prefix + paramName); testing() .waitAndAssertTraces( @@ -206,49 +207,8 @@ public void sayHello( equalTo( MessageIncubatingAttributes.MESSAGE_TYPE, "SENT"), equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L))))); - testing() - .waitAndAssertMetrics( - "io.opentelemetry.grpc-1.6", - "rpc.server.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfying( - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_SYSTEM, "grpc"), - equalTo( - RPC_GRPC_STATUS_CODE, - (long) Status.Code.OK.value())))))); - testing() - .waitAndAssertMetrics( - "io.opentelemetry.grpc-1.6", - "rpc.client.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfying( - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, server.getPort()), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_SYSTEM, "grpc"), - equalTo( - RPC_GRPC_STATUS_CODE, - (long) Status.Code.OK.value())))))); + + assertMetrics(server, Status.Code.OK); } @Test @@ -274,13 +234,14 @@ public void sayHello( AtomicReference response = new AtomicReference<>(); AtomicReference error = new AtomicReference<>(); + Helloworld.Request request = Helloworld.Request.newBuilder().setName("test").build(); testing() .runWithSpan( "parent", () -> { ListenableFuture future = Futures.transform( - client.sayHello(Helloworld.Request.newBuilder().setName("test").build()), + client.sayHello(request), resp -> { testing().runWithSpan("child", () -> {}); return resp; @@ -294,7 +255,8 @@ public void sayHello( }); assertThat(error).hasValue(null); - assertThat(response.get().getMessage()).isEqualTo("Hello test"); + Helloworld.Response res = response.get(); + assertThat(res.getMessage()).isEqualTo("Hello test"); testing() .waitAndAssertTraces( @@ -374,49 +336,8 @@ public void sayHello( span.hasName("child") .hasKind(SpanKind.INTERNAL) .hasParent(trace.getSpan(0)))); - testing() - .waitAndAssertMetrics( - "io.opentelemetry.grpc-1.6", - "rpc.server.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfying( - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_SYSTEM, "grpc"), - equalTo( - RPC_GRPC_STATUS_CODE, - (long) Status.Code.OK.value())))))); - testing() - .waitAndAssertMetrics( - "io.opentelemetry.grpc-1.6", - "rpc.client.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfying( - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, server.getPort()), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_SYSTEM, "grpc"), - equalTo( - RPC_GRPC_STATUS_CODE, - (long) Status.Code.OK.value())))))); + + assertMetrics(server, Status.Code.OK); } @Test @@ -443,12 +364,13 @@ public void sayHello( AtomicReference response = new AtomicReference<>(); AtomicReference error = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); + Helloworld.Request request = Helloworld.Request.newBuilder().setName("test").build(); testing() .runWithSpan( "parent", () -> client.sayHello( - Helloworld.Request.newBuilder().setName("test").build(), + request, new StreamObserver() { @Override public void onNext(Helloworld.Response r) { @@ -470,7 +392,8 @@ public void onCompleted() { latch.await(10, TimeUnit.SECONDS); assertThat(error).hasValue(null); - assertThat(response.get().getMessage()).isEqualTo("Hello test"); + Helloworld.Response res = response.get(); + assertThat(res.getMessage()).isEqualTo("Hello test"); testing() .waitAndAssertTraces( @@ -550,49 +473,8 @@ public void onCompleted() { span.hasName("child") .hasKind(SpanKind.INTERNAL) .hasParent(trace.getSpan(0)))); - testing() - .waitAndAssertMetrics( - "io.opentelemetry.grpc-1.6", - "rpc.server.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfying( - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_SYSTEM, "grpc"), - equalTo( - RPC_GRPC_STATUS_CODE, - (long) Status.Code.OK.value())))))); - testing() - .waitAndAssertMetrics( - "io.opentelemetry.grpc-1.6", - "rpc.client.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfying( - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, server.getPort()), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_SYSTEM, "grpc"), - equalTo( - RPC_GRPC_STATUS_CODE, - (long) Status.Code.OK.value())))))); + + assertMetrics(server, Status.Code.OK); } @ParameterizedTest @@ -613,8 +495,8 @@ public void sayHello( GreeterGrpc.GreeterBlockingStub client = GreeterGrpc.newBlockingStub(channel); - assertThatThrownBy( - () -> client.sayHello(Helloworld.Request.newBuilder().setName("error").build())) + Helloworld.Request request = Helloworld.Request.newBuilder().setName("error").build(); + assertThatThrownBy(() -> client.sayHello(request)) .isInstanceOfSatisfying( StatusRuntimeException.class, t -> { @@ -690,49 +572,8 @@ public void sayHello( span.hasException(status.getCause()); } }))); - testing() - .waitAndAssertMetrics( - "io.opentelemetry.grpc-1.6", - "rpc.server.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfying( - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_SYSTEM, "grpc"), - equalTo( - RPC_GRPC_STATUS_CODE, - (long) status.getCode().value())))))); - testing() - .waitAndAssertMetrics( - "io.opentelemetry.grpc-1.6", - "rpc.client.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfying( - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, server.getPort()), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_SYSTEM, "grpc"), - equalTo( - RPC_GRPC_STATUS_CODE, - (long) status.getCode().value())))))); + + assertMetrics(server, status.getCode()); } @ParameterizedTest @@ -752,9 +593,8 @@ public void sayHello( closer.add(() -> server.shutdownNow().awaitTermination()); GreeterGrpc.GreeterBlockingStub client = GreeterGrpc.newBlockingStub(channel); - - assertThatThrownBy( - () -> client.sayHello(Helloworld.Request.newBuilder().setName("error").build())) + Helloworld.Request request = Helloworld.Request.newBuilder().setName("error").build(); + assertThatThrownBy(() -> client.sayHello(request)) .isInstanceOfSatisfying( StatusRuntimeException.class, t -> { @@ -830,49 +670,8 @@ public void sayHello( equalTo(MessageIncubatingAttributes.MESSAGE_ID, 1L)); span.hasException(status.asRuntimeException()); }))); - testing() - .waitAndAssertMetrics( - "io.opentelemetry.grpc-1.6", - "rpc.server.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfying( - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_SYSTEM, "grpc"), - equalTo( - RPC_GRPC_STATUS_CODE, - (long) Status.Code.UNKNOWN.value())))))); - testing() - .waitAndAssertMetrics( - "io.opentelemetry.grpc-1.6", - "rpc.client.duration", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point.hasAttributesSatisfying( - equalTo(SERVER_ADDRESS, "localhost"), - equalTo(SERVER_PORT, server.getPort()), - equalTo(RPC_METHOD, "SayHello"), - equalTo(RPC_SERVICE, "example.Greeter"), - equalTo(RPC_SYSTEM, "grpc"), - equalTo( - RPC_GRPC_STATUS_CODE, - (long) Status.Code.UNKNOWN.value())))))); + + assertMetrics(server, Status.Code.UNKNOWN); } private static Stream provideErrorArguments() { @@ -971,12 +770,13 @@ public ClientCall interceptCall( AtomicReference response = new AtomicReference<>(); AtomicReference error = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); + Helloworld.Request request = Helloworld.Request.newBuilder().setName("test").build(); testing() .runWithSpan( "parent", () -> client.sayHello( - Helloworld.Request.newBuilder().setName("test").build(), + request, new StreamObserver() { @Override public void onNext(Helloworld.Response r) { @@ -1007,7 +807,8 @@ public void onCompleted() { latch.await(10, TimeUnit.SECONDS); assertThat(error).hasValue(null); - assertThat(response.get().getMessage()).isEqualTo("Hello test"); + Helloworld.Response res = response.get(); + assertThat(res.getMessage()).isEqualTo("Hello test"); testing() .waitAndAssertTraces( @@ -1105,17 +906,20 @@ public void sayMultipleHello( GreeterGrpc.GreeterStub client = GreeterGrpc.newStub(channel); IllegalStateException thrown = new IllegalStateException("illegal"); + AtomicReference response = new AtomicReference<>(); AtomicReference error = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); + Helloworld.Request request = Helloworld.Request.newBuilder().setName("test").build(); testing() .runWithSpan( "parent", () -> client.sayMultipleHello( - Helloworld.Request.newBuilder().setName("test").build(), + request, new StreamObserver() { @Override public void onNext(Helloworld.Response r) { + response.set(r); throw thrown; } @@ -1250,16 +1054,18 @@ public void onCompleted() { } }); - request.onNext( + ServerReflectionRequest serverReflectionRequest = ServerReflectionRequest.newBuilder() .setListServices("The content will not be checked?") - .build()); + .build(); + request.onNext(serverReflectionRequest); request.onCompleted(); latch.await(10, TimeUnit.SECONDS); assertThat(error).hasValue(null); - assertThat(response.get().getListServicesResponse().getService(0).getName()) + ServerReflectionResponse serverReflectionResponse = response.get(); + assertThat(serverReflectionResponse.getListServicesResponse().getService(0).getName()) .isEqualTo("grpc.reflection.v1alpha.ServerReflection"); testing() @@ -1368,11 +1174,8 @@ public void sayHello( GreeterGrpc.GreeterBlockingStub client = GreeterGrpc.newBlockingStub(channel); - Helloworld.Response response = - testing() - .runWithSpan( - "parent", - () -> client.sayHello(Helloworld.Request.newBuilder().setName("test").build())); + Helloworld.Request request = Helloworld.Request.newBuilder().setName("test").build(); + Helloworld.Response response = testing().runWithSpan("parent", () -> client.sayHello(request)); assertThat(response.getMessage()).isEqualTo("Hello test"); @@ -1696,4 +1499,153 @@ static List addExtraClientAttributes(AttributeAssertion... a } return result; } + + private void assertMetrics(Server server, Status.Code statusCode) { + boolean hasSizeMetric = statusCode == Status.Code.OK; + testing() + .waitAndAssertMetrics( + "io.opentelemetry.grpc-1.6", + "rpc.server.duration", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point.hasAttributesSatisfyingExactly( + equalTo(SERVER_ADDRESS, "localhost"), + satisfies( + SERVER_PORT, k -> k.isInstanceOf(Long.class)), + equalTo(RPC_METHOD, "SayHello"), + equalTo(RPC_SERVICE, "example.Greeter"), + equalTo(RPC_SYSTEM, "grpc"), + equalTo( + RPC_GRPC_STATUS_CODE, + (long) statusCode.value()), + equalTo(NETWORK_TYPE, "ipv4")))))); + + if (hasSizeMetric) { + testing() + .waitAndAssertMetrics( + "io.opentelemetry.grpc-1.6", + "rpc.server.request.size", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("By") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point.hasAttributesSatisfyingExactly( + equalTo(SERVER_ADDRESS, "localhost"), + satisfies( + SERVER_PORT, k -> k.isInstanceOf(Long.class)), + equalTo(RPC_METHOD, "SayHello"), + equalTo(RPC_SERVICE, "example.Greeter"), + equalTo(RPC_SYSTEM, "grpc"), + equalTo( + RPC_GRPC_STATUS_CODE, + (long) statusCode.value()), + equalTo(NETWORK_TYPE, "ipv4")))))); + testing() + .waitAndAssertMetrics( + "io.opentelemetry.grpc-1.6", + "rpc.server.response.size", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("By") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point.hasAttributesSatisfyingExactly( + equalTo(SERVER_ADDRESS, "localhost"), + satisfies( + SERVER_PORT, k -> k.isInstanceOf(Long.class)), + equalTo(RPC_METHOD, "SayHello"), + equalTo(RPC_SERVICE, "example.Greeter"), + equalTo(RPC_SYSTEM, "grpc"), + equalTo( + RPC_GRPC_STATUS_CODE, + (long) statusCode.value()), + equalTo(NETWORK_TYPE, "ipv4")))))); + } + + testing() + .waitAndAssertMetrics( + "io.opentelemetry.grpc-1.6", + "rpc.client.duration", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point.hasAttributesSatisfying( + equalTo(SERVER_ADDRESS, "localhost"), + equalTo(SERVER_PORT, server.getPort()), + equalTo(RPC_METHOD, "SayHello"), + equalTo(RPC_SERVICE, "example.Greeter"), + equalTo(RPC_SYSTEM, "grpc"), + equalTo( + RPC_GRPC_STATUS_CODE, + (long) statusCode.value())))))); + + testing() + .waitAndAssertMetrics( + "io.opentelemetry.grpc-1.6", + "rpc.client.request.size", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("By") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point.hasAttributesSatisfying( + equalTo(SERVER_ADDRESS, "localhost"), + equalTo(SERVER_PORT, server.getPort()), + equalTo(RPC_METHOD, "SayHello"), + equalTo(RPC_SERVICE, "example.Greeter"), + equalTo(RPC_SYSTEM, "grpc"), + equalTo( + RPC_GRPC_STATUS_CODE, + (long) statusCode.value())))))); + if (hasSizeMetric) { + testing() + .waitAndAssertMetrics( + "io.opentelemetry.grpc-1.6", + "rpc.client.response.size", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("By") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point.hasAttributesSatisfying( + equalTo(SERVER_ADDRESS, "localhost"), + equalTo(SERVER_PORT, server.getPort()), + equalTo(RPC_METHOD, "SayHello"), + equalTo(RPC_SERVICE, "example.Greeter"), + equalTo(RPC_SYSTEM, "grpc"), + equalTo( + RPC_GRPC_STATUS_CODE, + (long) statusCode.value())))))); + } + } }