Skip to content

Commit 4516222

Browse files
crossoverJieasweet-confluent
authored andcommitted
grpc support request/response size
1 parent 1115cda commit 4516222

File tree

14 files changed

+388
-7
lines changed

14 files changed

+388
-7
lines changed

instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcAttributesGetter.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,20 @@ public interface RpcAttributesGetter<REQUEST> {
2424

2525
@Nullable
2626
String getMethod(REQUEST request);
27+
28+
default int getClientRequestSize(REQUEST request) {
29+
return 0;
30+
}
31+
32+
default int getClientResponseSize(REQUEST request) {
33+
return 0;
34+
}
35+
36+
default int getServerRequestSize(REQUEST request) {
37+
return 0;
38+
}
39+
40+
default int getServerResponseSize(REQUEST request) {
41+
return 0;
42+
}
2743
}

instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcClientMetrics.java

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import io.opentelemetry.api.common.Attributes;
1212
import io.opentelemetry.api.metrics.DoubleHistogram;
1313
import io.opentelemetry.api.metrics.DoubleHistogramBuilder;
14+
import io.opentelemetry.api.metrics.LongHistogram;
15+
import io.opentelemetry.api.metrics.LongHistogramBuilder;
1416
import io.opentelemetry.api.metrics.Meter;
1517
import io.opentelemetry.context.Context;
1618
import io.opentelemetry.context.ContextKey;
@@ -35,6 +37,8 @@ public final class RpcClientMetrics implements OperationListener {
3537
private static final Logger logger = Logger.getLogger(RpcClientMetrics.class.getName());
3638

3739
private final DoubleHistogram clientDurationHistogram;
40+
private final LongHistogram clientRequestSize;
41+
private final LongHistogram clientResponseSize;
3842

3943
private RpcClientMetrics(Meter meter) {
4044
DoubleHistogramBuilder durationBuilder =
@@ -44,6 +48,24 @@ private RpcClientMetrics(Meter meter) {
4448
.setUnit("ms");
4549
RpcMetricsAdvice.applyClientDurationAdvice(durationBuilder);
4650
clientDurationHistogram = durationBuilder.build();
51+
52+
LongHistogramBuilder requestSizeBuilder =
53+
meter
54+
.histogramBuilder("rpc.client.request.size")
55+
.setUnit("By")
56+
.setDescription("Measures the size of RPC request messages (uncompressed).")
57+
.ofLongs();
58+
RpcMetricsAdvice.applyClientRequestSizeAdvice(requestSizeBuilder);
59+
clientRequestSize = requestSizeBuilder.build();
60+
61+
LongHistogramBuilder responseSizeBuilder =
62+
meter
63+
.histogramBuilder("rpc.client.response.size")
64+
.setUnit("By")
65+
.setDescription("Measures the size of RPC response messages (uncompressed).")
66+
.ofLongs();
67+
RpcMetricsAdvice.applyClientRequestSizeAdvice(responseSizeBuilder);
68+
clientResponseSize = responseSizeBuilder.build();
4769
}
4870

4971
/**
@@ -72,10 +94,21 @@ public void onEnd(Context context, Attributes endAttributes, long endNanos) {
7294
context);
7395
return;
7496
}
97+
Attributes attributes = state.startAttributes().toBuilder().putAll(endAttributes).build();
7598
clientDurationHistogram.record(
76-
(endNanos - state.startTimeNanos()) / NANOS_PER_MS,
77-
state.startAttributes().toBuilder().putAll(endAttributes).build(),
78-
context);
99+
(endNanos - state.startTimeNanos()) / NANOS_PER_MS, attributes, context);
100+
101+
Long rpcClientRequestBodySize = RpcMessageBodySizeUtil.getRpcClientRequestBodySize(
102+
endAttributes, state.startAttributes());
103+
if (rpcClientRequestBodySize != null && rpcClientRequestBodySize > 0) {
104+
clientRequestSize.record(rpcClientRequestBodySize, attributes, context);
105+
}
106+
107+
Long rpcClientResponseBodySize = RpcMessageBodySizeUtil.getRpcClientResponseBodySize(
108+
endAttributes, state.startAttributes());
109+
if (rpcClientResponseBodySize != null && rpcClientResponseBodySize > 0) {
110+
clientResponseSize.record(rpcClientResponseBodySize, attributes, context);
111+
}
79112
}
80113

81114
@AutoValue

instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcCommonAttributesExtractor.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ abstract class RpcCommonAttributesExtractor<REQUEST, RESPONSE>
2020
static final AttributeKey<String> RPC_METHOD = AttributeKey.stringKey("rpc.method");
2121
static final AttributeKey<String> RPC_SERVICE = AttributeKey.stringKey("rpc.service");
2222
static final AttributeKey<String> RPC_SYSTEM = AttributeKey.stringKey("rpc.system");
23+
static final AttributeKey<Long> RPC_CLIENT_REQUEST_BODY_SIZE =
24+
AttributeKey.longKey("rpc.client.request.body.size");
25+
static final AttributeKey<Long> RPC_CLIENT_RESPONSE_BODY_SIZE =
26+
AttributeKey.longKey("rpc.client.response.body.size");
27+
static final AttributeKey<Long> RPC_SERVER_REQUEST_BODY_SIZE =
28+
AttributeKey.longKey("rpc.server.request.body.size");
29+
static final AttributeKey<Long> RPC_SERVER_RESPONSE_BODY_SIZE =
30+
AttributeKey.longKey("rpc.server.response.body.size");
2331

2432
private final RpcAttributesGetter<REQUEST> getter;
2533

@@ -41,6 +49,14 @@ public final void onEnd(
4149
REQUEST request,
4250
@Nullable RESPONSE response,
4351
@Nullable Throwable error) {
44-
// No response attributes
52+
internalSet(attributes, RPC_CLIENT_REQUEST_BODY_SIZE,
53+
(long) getter.getClientRequestSize(request));
54+
internalSet(attributes, RPC_CLIENT_RESPONSE_BODY_SIZE,
55+
(long) getter.getClientResponseSize(request));
56+
57+
internalSet(attributes, RPC_SERVER_REQUEST_BODY_SIZE,
58+
(long) getter.getServerRequestSize(request));
59+
internalSet(attributes, RPC_SERVER_RESPONSE_BODY_SIZE,
60+
(long) getter.getServerResponseSize(request));
4561
}
4662
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.incubator.semconv.rpc;
7+
8+
import io.opentelemetry.api.common.AttributeKey;
9+
import io.opentelemetry.api.common.Attributes;
10+
import javax.annotation.Nullable;
11+
12+
final class RpcMessageBodySizeUtil {
13+
14+
@Nullable
15+
static Long getRpcClientRequestBodySize(Attributes... attributesList) {
16+
return getAttribute(RpcCommonAttributesExtractor.RPC_CLIENT_REQUEST_BODY_SIZE, attributesList);
17+
}
18+
19+
@Nullable
20+
static Long getRpcClientResponseBodySize(Attributes... attributesList) {
21+
return getAttribute(
22+
RpcCommonAttributesExtractor.RPC_CLIENT_RESPONSE_BODY_SIZE, attributesList);
23+
}
24+
25+
@Nullable
26+
static Long getRpcServerRequestBodySize(Attributes... attributesList) {
27+
return getAttribute(RpcCommonAttributesExtractor.RPC_SERVER_REQUEST_BODY_SIZE, attributesList);
28+
}
29+
@Nullable
30+
static Long getRpcServerResponseBodySize(Attributes... attributesList) {
31+
return getAttribute(
32+
RpcCommonAttributesExtractor.RPC_SERVER_RESPONSE_BODY_SIZE, attributesList);
33+
}
34+
35+
@Nullable
36+
private static <T> T getAttribute(AttributeKey<T> key, Attributes... attributesList) {
37+
for (Attributes attributes : attributesList) {
38+
T value = attributes.get(key);
39+
if (value != null) {
40+
return value;
41+
}
42+
}
43+
return null;
44+
}
45+
46+
private RpcMessageBodySizeUtil() {}
47+
}

instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcMetricsAdvice.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77

88
import io.opentelemetry.api.common.AttributeKey;
99
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleHistogramBuilder;
10+
import io.opentelemetry.api.incubator.metrics.ExtendedLongHistogramBuilder;
1011
import io.opentelemetry.api.metrics.DoubleHistogramBuilder;
12+
import io.opentelemetry.api.metrics.LongHistogramBuilder;
1113
import io.opentelemetry.semconv.NetworkAttributes;
1214
import io.opentelemetry.semconv.ServerAttributes;
1315
import java.util.Arrays;
@@ -55,6 +57,43 @@ static void applyServerDurationAdvice(DoubleHistogramBuilder builder) {
5557
ServerAttributes.SERVER_ADDRESS,
5658
ServerAttributes.SERVER_PORT));
5759
}
60+
static void applyClientRequestSizeAdvice(LongHistogramBuilder builder) {
61+
if (!(builder instanceof ExtendedLongHistogramBuilder)) {
62+
return;
63+
}
64+
// the list of recommended metrics attributes is from
65+
// https://github.com/open-telemetry/semantic-conventions/blob/main/docs/rpc/rpc-metrics.md
66+
((ExtendedLongHistogramBuilder) builder)
67+
.setAttributesAdvice(
68+
Arrays.asList(
69+
RpcCommonAttributesExtractor.RPC_SYSTEM,
70+
RpcCommonAttributesExtractor.RPC_SERVICE,
71+
RpcCommonAttributesExtractor.RPC_METHOD,
72+
RPC_GRPC_STATUS_CODE,
73+
NetworkAttributes.NETWORK_TYPE,
74+
NetworkAttributes.NETWORK_TRANSPORT,
75+
ServerAttributes.SERVER_ADDRESS,
76+
ServerAttributes.SERVER_PORT));
77+
}
78+
79+
static void applyServerRequestSizeAdvice(LongHistogramBuilder builder) {
80+
if (!(builder instanceof ExtendedLongHistogramBuilder)) {
81+
return;
82+
}
83+
// the list of recommended metrics attributes is from
84+
// https://github.com/open-telemetry/semantic-conventions/blob/main/docs/rpc/rpc-metrics.md
85+
((ExtendedLongHistogramBuilder) builder)
86+
.setAttributesAdvice(
87+
Arrays.asList(
88+
RpcCommonAttributesExtractor.RPC_SYSTEM,
89+
RpcCommonAttributesExtractor.RPC_SERVICE,
90+
RpcCommonAttributesExtractor.RPC_METHOD,
91+
RPC_GRPC_STATUS_CODE,
92+
NetworkAttributes.NETWORK_TYPE,
93+
NetworkAttributes.NETWORK_TRANSPORT,
94+
ServerAttributes.SERVER_ADDRESS,
95+
ServerAttributes.SERVER_PORT));
96+
}
5897

5998
private RpcMetricsAdvice() {}
6099
}

instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcServerMetrics.java

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import io.opentelemetry.api.common.Attributes;
1212
import io.opentelemetry.api.metrics.DoubleHistogram;
1313
import io.opentelemetry.api.metrics.DoubleHistogramBuilder;
14+
import io.opentelemetry.api.metrics.LongHistogram;
15+
import io.opentelemetry.api.metrics.LongHistogramBuilder;
1416
import io.opentelemetry.api.metrics.Meter;
1517
import io.opentelemetry.context.Context;
1618
import io.opentelemetry.context.ContextKey;
@@ -35,6 +37,8 @@ public final class RpcServerMetrics implements OperationListener {
3537
private static final Logger logger = Logger.getLogger(RpcServerMetrics.class.getName());
3638

3739
private final DoubleHistogram serverDurationHistogram;
40+
private final LongHistogram serverRequestSize;
41+
private final LongHistogram serverResponseSize;
3842

3943
private RpcServerMetrics(Meter meter) {
4044
DoubleHistogramBuilder durationBuilder =
@@ -44,6 +48,24 @@ private RpcServerMetrics(Meter meter) {
4448
.setUnit("ms");
4549
RpcMetricsAdvice.applyServerDurationAdvice(durationBuilder);
4650
serverDurationHistogram = durationBuilder.build();
51+
52+
LongHistogramBuilder requestSizeBuilder =
53+
meter
54+
.histogramBuilder("rpc.server.request.size")
55+
.setUnit("By")
56+
.setDescription("Measures the size of RPC request messages (uncompressed).")
57+
.ofLongs();
58+
RpcMetricsAdvice.applyServerRequestSizeAdvice(requestSizeBuilder);
59+
serverRequestSize = requestSizeBuilder.build();
60+
61+
LongHistogramBuilder responseSizeBuilder =
62+
meter
63+
.histogramBuilder("rpc.server.response.size")
64+
.setUnit("By")
65+
.setDescription("Measures the size of RPC response messages (uncompressed).")
66+
.ofLongs();
67+
RpcMetricsAdvice.applyServerRequestSizeAdvice(responseSizeBuilder);
68+
serverResponseSize = responseSizeBuilder.build();
4769
}
4870

4971
/**
@@ -72,10 +94,21 @@ public void onEnd(Context context, Attributes endAttributes, long endNanos) {
7294
context);
7395
return;
7496
}
97+
Attributes attributes = state.startAttributes().toBuilder().putAll(endAttributes).build();
7598
serverDurationHistogram.record(
76-
(endNanos - state.startTimeNanos()) / NANOS_PER_MS,
77-
state.startAttributes().toBuilder().putAll(endAttributes).build(),
78-
context);
99+
(endNanos - state.startTimeNanos()) / NANOS_PER_MS, attributes, context);
100+
101+
Long rpcServerRequestBodySize = RpcMessageBodySizeUtil.getRpcServerRequestBodySize(
102+
endAttributes, state.startAttributes());
103+
if (rpcServerRequestBodySize != null && rpcServerRequestBodySize >0 ) {
104+
serverRequestSize.record(rpcServerRequestBodySize, attributes, context);
105+
}
106+
107+
Long rpcServerResponseBodySize = RpcMessageBodySizeUtil.getRpcServerResponseBodySize(
108+
endAttributes, state.startAttributes());
109+
if (rpcServerResponseBodySize != null && rpcServerResponseBodySize > 0) {
110+
serverResponseSize.record(rpcServerResponseBodySize, attributes, context);
111+
}
79112
}
80113

81114
@AutoValue

instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcClientMetricsTest.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import io.opentelemetry.context.Context;
1717
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
1818
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
19+
import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions;
1920
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
2021
import io.opentelemetry.semconv.NetworkAttributes;
2122
import io.opentelemetry.semconv.ServerAttributes;
@@ -46,6 +47,8 @@ void collectsMetrics() {
4647
.put(ServerAttributes.SERVER_PORT, 8080)
4748
.put(NetworkAttributes.NETWORK_TRANSPORT, "tcp")
4849
.put(NetworkAttributes.NETWORK_TYPE, "ipv4")
50+
.put(RpcCommonAttributesExtractor.RPC_CLIENT_REQUEST_BODY_SIZE, 10)
51+
.put(RpcCommonAttributesExtractor.RPC_CLIENT_RESPONSE_BODY_SIZE, 20)
4952
.build();
5053

5154
Attributes responseAttributes2 =
@@ -76,6 +79,60 @@ void collectsMetrics() {
7679

7780
assertThat(metricReader.collectAllMetrics())
7881
.satisfiesExactlyInAnyOrder(
82+
metric ->
83+
OpenTelemetryAssertions.assertThat(metric)
84+
.hasName("rpc.client.response.size")
85+
.hasUnit("By")
86+
.hasDescription("Measures the size of RPC response messages (uncompressed).")
87+
.hasHistogramSatisfying(
88+
histogram ->
89+
histogram.hasPointsSatisfying(
90+
point ->
91+
point.hasSum(20 /* bytes */)
92+
.hasAttributesSatisfying(
93+
equalTo(RpcIncubatingAttributes.RPC_SYSTEM, "grpc"),
94+
equalTo(
95+
RpcIncubatingAttributes.RPC_SERVICE,
96+
"myservice.EchoService"),
97+
equalTo(
98+
RpcIncubatingAttributes.RPC_METHOD,
99+
"exampleMethod"),
100+
equalTo(ServerAttributes.SERVER_ADDRESS, "example.com"),
101+
equalTo(ServerAttributes.SERVER_PORT, 8080),
102+
equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp"),
103+
equalTo(NetworkAttributes.NETWORK_TYPE, "ipv4"))
104+
.hasExemplarsSatisfying(
105+
exemplar ->
106+
exemplar
107+
.hasTraceId("ff01020304050600ff0a0b0c0d0e0f00")
108+
.hasSpanId("090a0b0c0d0e0f00")))),
109+
metric ->
110+
OpenTelemetryAssertions.assertThat(metric)
111+
.hasName("rpc.client.request.size")
112+
.hasUnit("By")
113+
.hasDescription("Measures the size of RPC request messages (uncompressed).")
114+
.hasHistogramSatisfying(
115+
histogram ->
116+
histogram.hasPointsSatisfying(
117+
point ->
118+
point.hasSum(10 /* bytes */)
119+
.hasAttributesSatisfying(
120+
equalTo(RpcIncubatingAttributes.RPC_SYSTEM, "grpc"),
121+
equalTo(
122+
RpcIncubatingAttributes.RPC_SERVICE,
123+
"myservice.EchoService"),
124+
equalTo(
125+
RpcIncubatingAttributes.RPC_METHOD,
126+
"exampleMethod"),
127+
equalTo(ServerAttributes.SERVER_ADDRESS, "example.com"),
128+
equalTo(ServerAttributes.SERVER_PORT, 8080),
129+
equalTo(NetworkAttributes.NETWORK_TRANSPORT, "tcp"),
130+
equalTo(NetworkAttributes.NETWORK_TYPE, "ipv4"))
131+
.hasExemplarsSatisfying(
132+
exemplar ->
133+
exemplar
134+
.hasTraceId("ff01020304050600ff0a0b0c0d0e0f00")
135+
.hasSpanId("090a0b0c0d0e0f00")))),
79136
metric ->
80137
assertThat(metric)
81138
.hasName("rpc.client.duration")

0 commit comments

Comments
 (0)