3030
3131package com .google .showcase .v1beta1 .it ;
3232
33- import static org .junit .jupiter . api . Assertions .assertThrows ;
33+ import static org .junit .Assert .assertThrows ;
3434
3535import com .google .api .client .http .javanet .NetHttpTransport ;
36+ import com .google .api .core .ApiFunction ;
3637import com .google .api .core .ApiFuture ;
3738import com .google .api .gax .core .NoCredentialsProvider ;
3839import com .google .api .gax .grpc .InstantiatingGrpcChannelProvider ;
5960import com .google .showcase .v1beta1 .stub .EchoStub ;
6061import com .google .showcase .v1beta1 .stub .EchoStubSettings ;
6162import io .grpc .ManagedChannelBuilder ;
63+ import io .grpc .opentelemetry .GrpcOpenTelemetry ;
6264import io .opentelemetry .api .OpenTelemetry ;
6365import io .opentelemetry .api .common .AttributeKey ;
6466import io .opentelemetry .api .common .Attributes ;
7678import java .util .List ;
7779import java .util .Map ;
7880import java .util .Optional ;
81+ import java .util .Set ;
7982import java .util .concurrent .ExecutionException ;
8083import java .util .concurrent .TimeUnit ;
8184import java .util .function .Predicate ;
85+ import java .util .stream .Collectors ;
8286import org .junit .jupiter .api .AfterEach ;
8387import org .junit .jupiter .api .Assertions ;
8488import 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}
0 commit comments