Skip to content

Commit 64ecb32

Browse files
committed
chore: Add test case testing with GrpcOpenTelemetry
1 parent 2bb0ee0 commit 64ecb32

File tree

4 files changed

+148
-52
lines changed

4 files changed

+148
-52
lines changed

java-showcase/gapic-showcase/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@
9696
<groupId>com.google.protobuf</groupId>
9797
<artifactId>protobuf-java</artifactId>
9898
</dependency>
99+
<!-- Add the grpc-java opentelemetry module to test for clients using GrpcOpenTelemetry -->
100+
<dependency>
101+
<groupId>io.grpc</groupId>
102+
<artifactId>grpc-opentelemetry</artifactId>
103+
</dependency>
99104
<dependency>
100105
<groupId>com.google.api.grpc</groupId>
101106
<artifactId>proto-google-common-protos</artifactId>

java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java

Lines changed: 92 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@
3030

3131
package com.google.showcase.v1beta1.it;
3232

33-
import static org.junit.jupiter.api.Assertions.assertThrows;
33+
import static org.junit.Assert.assertThrows;
3434

3535
import com.google.api.client.http.javanet.NetHttpTransport;
36+
import com.google.api.core.ApiFunction;
3637
import com.google.api.core.ApiFuture;
3738
import com.google.api.gax.core.NoCredentialsProvider;
3839
import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider;
@@ -59,6 +60,7 @@
5960
import com.google.showcase.v1beta1.stub.EchoStub;
6061
import com.google.showcase.v1beta1.stub.EchoStubSettings;
6162
import io.grpc.ManagedChannelBuilder;
63+
import io.grpc.opentelemetry.GrpcOpenTelemetry;
6264
import io.opentelemetry.api.OpenTelemetry;
6365
import io.opentelemetry.api.common.AttributeKey;
6466
import io.opentelemetry.api.common.Attributes;
@@ -76,9 +78,11 @@
7678
import java.util.List;
7779
import java.util.Map;
7880
import java.util.Optional;
81+
import java.util.Set;
7982
import java.util.concurrent.ExecutionException;
8083
import java.util.concurrent.TimeUnit;
8184
import java.util.function.Predicate;
85+
import java.util.stream.Collectors;
8286
import org.junit.jupiter.api.AfterEach;
8387
import org.junit.jupiter.api.Assertions;
8488
import org.junit.jupiter.api.BeforeEach;
@@ -102,8 +106,13 @@ class ITOtelMetrics {
102106
private static final String OPERATION_COUNT = SERVICE_NAME + "/operation_count";
103107
private static final String ATTEMPT_LATENCY = SERVICE_NAME + "/attempt_latency";
104108
private static final String OPERATION_LATENCY = SERVICE_NAME + "/operation_latency";
105-
private static final int NUM_DEFAULT_METRICS = 4;
106-
private static final int NUM_COLLECTION_FLUSH_ATTEMPTS = 10;
109+
private static final Set<String> GAX_METRICS =
110+
ImmutableSet.of(ATTEMPT_COUNT, OPERATION_COUNT, ATTEMPT_LATENCY, OPERATION_LATENCY);
111+
112+
// Gax provides four metrics by default. This number may change as new metrics are added.
113+
private static final int NUM_GAX_OTEL_METRICS = 4;
114+
private static final int NUM_DEFAULT_FLUSH_ATTEMPTS = 10;
115+
107116
private InMemoryMetricReader inMemoryMetricReader;
108117
private EchoClient grpcClient;
109118
private EchoClient httpClient;
@@ -157,7 +166,8 @@ void setup() throws Exception {
157166
}
158167

159168
@AfterEach
160-
void cleanup() throws InterruptedException {
169+
void cleanup() throws InterruptedException, IOException {
170+
inMemoryMetricReader.close();
161171
inMemoryMetricReader.shutdown();
162172

163173
grpcClient.close();
@@ -286,17 +296,25 @@ private List<MetricData> getMetricDataList() throws InterruptedException {
286296
*/
287297
private List<MetricData> getMetricDataList(InMemoryMetricReader metricReader)
288298
throws InterruptedException {
289-
for (int i = 0; i < NUM_COLLECTION_FLUSH_ATTEMPTS; i++) {
299+
for (int i = 0; i < NUM_DEFAULT_FLUSH_ATTEMPTS; i++) {
290300
Thread.sleep(1000L);
291301
List<MetricData> metricData = new ArrayList<>(metricReader.collectAllMetrics());
292-
if (metricData.size() == NUM_DEFAULT_METRICS) {
302+
// Depending on the OpenTelemetry instance (i.e. OpenTelemetry, GrpcOpenTelemetry, etc.)
303+
// there may be additional metrics recorded. Only check to ensure the Gax Metrics
304+
// are recorded properly. Any additional metrics are fine to be passed.
305+
if (metricData.size() >= NUM_GAX_OTEL_METRICS && areAllGaxMetricsRecorded(metricData)) {
293306
return metricData;
294307
}
295308
}
296-
Assertions.fail("Unable to collect all the metrics required for the test");
309+
Assertions.fail("Unable to collect all the GAX metrics required for the test");
297310
return new ArrayList<>();
298311
}
299312

313+
private boolean areAllGaxMetricsRecorded(List<MetricData> metricData) {
314+
return metricData.stream().filter(data -> GAX_METRICS.contains(data.getName())).count()
315+
== NUM_GAX_OTEL_METRICS;
316+
}
317+
300318
@Test
301319
void testGrpc_operationSucceeded_recordsMetrics() throws InterruptedException {
302320
int attemptCount = 1;
@@ -830,7 +848,73 @@ void recordsCustomAttributes() throws InterruptedException, IOException {
830848
randomAttributeKey2,
831849
randomAttributeValue2);
832850
verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
851+
}
833852

834-
inMemoryMetricReader.close();
853+
// This test case uses GrpcOpenTelemetry from grpc-java and includes additional grpc-java specific
854+
// metrics. This test case ensures that the `setSampledToLocalTracing` set to true will ensure
855+
// that
856+
// the gRPC full method name in the stub is recorded (not recorded as `other`).
857+
@Test
858+
void grpcOpenTelemetryImplementation_setSampledToLocalTracing_methodFullNameIsRecorded()
859+
throws Exception {
860+
SdkMeterProvider sdkMeterProvider =
861+
SdkMeterProvider.builder().registerMetricReader(inMemoryMetricReader).build();
862+
863+
OpenTelemetry openTelemetry =
864+
OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProvider).build();
865+
866+
GrpcOpenTelemetry grpcOpenTelemetry = GrpcOpenTelemetry.newBuilder().sdk(openTelemetry).build();
867+
868+
// Java-Spanner configures the gRPCTransportChannelProvider with gRPCOpenTelemetry
869+
// This setup below is copied from their implementation
870+
InstantiatingGrpcChannelProvider.Builder builder =
871+
EchoSettings.defaultGrpcTransportProviderBuilder();
872+
ApiFunction<ManagedChannelBuilder, ManagedChannelBuilder> channelConfigurator =
873+
builder.getChannelConfigurator();
874+
builder.setChannelConfigurator(
875+
b -> {
876+
b.usePlaintext();
877+
grpcOpenTelemetry.configureChannelBuilder(b);
878+
if (channelConfigurator != null) {
879+
return channelConfigurator.apply(b);
880+
}
881+
return b;
882+
});
883+
884+
OpenTelemetryMetricsRecorder otelMetricsRecorder =
885+
new OpenTelemetryMetricsRecorder(openTelemetry, SERVICE_NAME);
886+
887+
// Create a custom EchoClient that is different from what is created by default in setup()
888+
EchoClient echoClient =
889+
TestClientInitializer.createGrpcEchoClientOpentelemetry(
890+
new MetricsTracerFactory(otelMetricsRecorder), builder.build());
891+
892+
EchoRequest echoRequest =
893+
EchoRequest.newBuilder().setContent("test_grpc_request_succeeded").build();
894+
echoClient.echo(echoRequest);
895+
896+
List<MetricData> metricDataList = getMetricDataList();
897+
898+
String gRPCMetricNamePrefix = "grpc.";
899+
String grpcMethodNameAttributeKey = "grpc.method";
900+
901+
List<MetricData> grpcMetricDataList =
902+
metricDataList.stream()
903+
.filter(x -> x.getName().startsWith(gRPCMetricNamePrefix))
904+
.collect(Collectors.toList());
905+
Truth.assertThat(grpcMetricDataList).isNotEmpty();
906+
for (MetricData grpcMetricData : grpcMetricDataList) {
907+
List<PointData> pointDataList = new ArrayList<>(grpcMetricData.getData().getPoints());
908+
909+
for (PointData pointData : pointDataList) {
910+
String methodName =
911+
pointData.getAttributes().get(AttributeKey.stringKey(grpcMethodNameAttributeKey));
912+
Truth.assertThat(methodName).doesNotMatch("other");
913+
Truth.assertThat(methodName).matches("^google.showcase.v1beta1.Echo/.*$");
914+
}
915+
}
916+
917+
echoClient.close();
918+
echoClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS);
835919
}
836920
}
Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,40 @@
1-
/*
2-
* Copyright 2025 Google LLC
3-
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
7-
*
8-
* https://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
15-
*/
16-
17-
package com.google.showcase.v1beta1.it.logging;
18-
19-
import ch.qos.logback.classic.spi.ILoggingEvent;
20-
import ch.qos.logback.core.AppenderBase;
21-
import java.util.ArrayList;
22-
import java.util.List;
23-
24-
/** Logback appender used to set up tests. */
25-
public class TestAppender extends AppenderBase<ILoggingEvent> {
26-
public List<ILoggingEvent> events = new ArrayList<>();
27-
28-
@Override
29-
protected void append(ILoggingEvent eventObject) {
30-
// triggering Logback to capture the current MDC context and store it with the log event
31-
// the default ListAppender does not capture MDC contents
32-
eventObject.getMDCPropertyMap();
33-
34-
events.add(eventObject);
35-
}
36-
37-
public void clearEvents() {
38-
events.clear();
39-
}
40-
}
1+
///*
2+
// * Copyright 2025 Google LLC
3+
// *
4+
// * Licensed under the Apache License, Version 2.0 (the "License");
5+
// * you may not use this file except in compliance with the License.
6+
// * You may obtain a copy of the License at
7+
// *
8+
// * https://www.apache.org/licenses/LICENSE-2.0
9+
// *
10+
// * Unless required by applicable law or agreed to in writing, software
11+
// * distributed under the License is distributed on an "AS IS" BASIS,
12+
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// * See the License for the specific language governing permissions and
14+
// * limitations under the License.
15+
// */
16+
//
17+
//package com.google.showcase.v1beta1.it.logging;
18+
//
19+
//import ch.qos.logback.classic.spi.ILoggingEvent;
20+
//import ch.qos.logback.core.AppenderBase;
21+
//import java.util.ArrayList;
22+
//import java.util.List;
23+
//
24+
///** Logback appender used to set up tests. */
25+
//public class TestAppender extends AppenderBase<ILoggingEvent> {
26+
// public List<ILoggingEvent> events = new ArrayList<>();
27+
//
28+
// @Override
29+
// protected void append(ILoggingEvent eventObject) {
30+
// // triggering Logback to capture the current MDC context and store it with the log event
31+
// // the default ListAppender does not capture MDC contents
32+
// eventObject.getMDCPropertyMap();
33+
//
34+
// events.add(eventObject);
35+
// }
36+
//
37+
// public void clearEvents() {
38+
// events.clear();
39+
// }
40+
//}

java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/util/TestClientInitializer.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.api.gax.longrunning.OperationTimedPollAlgorithm;
2424
import com.google.api.gax.retrying.RetrySettings;
2525
import com.google.api.gax.rpc.StatusCode;
26+
import com.google.api.gax.rpc.TransportChannelProvider;
2627
import com.google.api.gax.rpc.UnaryCallSettings;
2728
import com.google.api.gax.tracing.ApiTracerFactory;
2829
import com.google.common.collect.ImmutableList;
@@ -289,14 +290,20 @@ public static ComplianceClient createHttpJsonComplianceClient(
289290

290291
public static EchoClient createGrpcEchoClientOpentelemetry(ApiTracerFactory metricsTracerFactory)
291292
throws Exception {
293+
return createGrpcEchoClientOpentelemetry(
294+
metricsTracerFactory,
295+
EchoSettings.defaultGrpcTransportProviderBuilder()
296+
.setChannelConfigurator(ManagedChannelBuilder::usePlaintext)
297+
.build());
298+
}
292299

300+
public static EchoClient createGrpcEchoClientOpentelemetry(
301+
ApiTracerFactory metricsTracerFactory, TransportChannelProvider transportChannelProvider)
302+
throws Exception {
293303
EchoSettings grpcEchoSettings =
294304
EchoSettings.newBuilder()
295305
.setCredentialsProvider(NoCredentialsProvider.create())
296-
.setTransportChannelProvider(
297-
EchoSettings.defaultGrpcTransportProviderBuilder()
298-
.setChannelConfigurator(ManagedChannelBuilder::usePlaintext)
299-
.build())
306+
.setTransportChannelProvider(transportChannelProvider)
300307
.setEndpoint(DEFAULT_GRPC_ENDPOINT)
301308
.build();
302309

0 commit comments

Comments
 (0)