Skip to content

Commit f122c6f

Browse files
lqiu96blakeli0
andauthoredJun 3, 2024··
feat: Allow Adding Client Level Attributes to MetricsTracerFactory (#2614)
Allow the MetricsTracerFactory to take in a second parameter (Map of attributes) that will be treated as client level attributes. These attributes will be added to every single MetricsTracer created throughout the lifecycle of the client. Was able to verify this behavior inside Cloud Monitoring: ![image](https://github.com/googleapis/sdk-platform-java/assets/6621793/832f485e-2834-4765-b381-9a15e2913412) Additional Attribute was recorded. Via: ``` InstantiatingGrpcChannelProvider channelProvider = InstantiatingGrpcChannelProvider.newBuilder().build(); Map<String, String> clientAttributesMapping = new HashMap<>(); clientAttributesMapping.put("directpath_enabled", String.valueOf(channelProvider.canUseDirectPath())); ... options .setApiTracerFactory(new MetricsTracerFactory(recorder, clientAttributesMapping)) .build(); ``` --------- Co-authored-by: Blake Li <blakeli@google.com>
1 parent dc88ea2 commit f122c6f

File tree

6 files changed

+448
-105
lines changed

6 files changed

+448
-105
lines changed
 

‎gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java

+59-19
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import com.google.api.core.ApiFunction;
3333
import com.google.api.core.BetaApi;
3434
import com.google.api.core.InternalApi;
35-
import com.google.api.core.InternalExtensionOnly;
3635
import com.google.api.gax.core.ExecutorProvider;
3736
import com.google.api.gax.rpc.FixedHeaderProvider;
3837
import com.google.api.gax.rpc.HeaderProvider;
@@ -82,13 +81,26 @@
8281
* <p>The client lib header and generator header values are used to form a value that goes into the
8382
* http header of requests to the service.
8483
*/
85-
@InternalExtensionOnly
8684
public final class InstantiatingGrpcChannelProvider implements TransportChannelProvider {
85+
86+
static String systemProductName;
87+
88+
static {
89+
try {
90+
systemProductName =
91+
Files.asCharSource(new File("/sys/class/dmi/id/product_name"), StandardCharsets.UTF_8)
92+
.readFirstLine();
93+
} catch (IOException e) {
94+
// If not on Compute Engine, FileNotFoundException will be thrown. Use empty string
95+
// as it won't match with the GCE_PRODUCTION_NAME constants
96+
systemProductName = "";
97+
}
98+
}
99+
87100
@VisibleForTesting
88101
static final Logger LOG = Logger.getLogger(InstantiatingGrpcChannelProvider.class.getName());
89102

90-
private static final String DIRECT_PATH_ENV_DISABLE_DIRECT_PATH =
91-
"GOOGLE_CLOUD_DISABLE_DIRECT_PATH";
103+
static final String DIRECT_PATH_ENV_DISABLE_DIRECT_PATH = "GOOGLE_CLOUD_DISABLE_DIRECT_PATH";
92104
private static final String DIRECT_PATH_ENV_ENABLE_XDS = "GOOGLE_CLOUD_ENABLE_DIRECT_PATH_XDS";
93105
static final long DIRECT_PATH_KEEP_ALIVE_TIME_SECONDS = 3600;
94106
static final long DIRECT_PATH_KEEP_ALIVE_TIMEOUT_SECONDS = 20;
@@ -147,6 +159,19 @@ private InstantiatingGrpcChannelProvider(Builder builder) {
147159
: builder.directPathServiceConfig;
148160
}
149161

162+
/**
163+
* Package-Private constructor that is only visible for testing DirectPath functionality inside
164+
* tests. This overrides the computed systemProductName when the class is initialized to help
165+
* configure the result of {@link #isOnComputeEngine()} check.
166+
*
167+
* <p>If productName is null, that represents the result of an IOException
168+
*/
169+
@VisibleForTesting
170+
InstantiatingGrpcChannelProvider(Builder builder, String productName) {
171+
this(builder);
172+
systemProductName = productName;
173+
}
174+
150175
/**
151176
* @deprecated If executor is not set, this channel provider will create channels with default
152177
* grpc executor.
@@ -257,8 +282,8 @@ private boolean isDirectPathEnabled() {
257282
return false;
258283
}
259284

260-
@VisibleForTesting
261-
boolean isDirectPathXdsEnabled() {
285+
@InternalApi
286+
public boolean isDirectPathXdsEnabled() {
262287
// Method 1: Enable DirectPath xDS by option.
263288
if (Boolean.TRUE.equals(attemptDirectPathXds)) {
264289
return true;
@@ -320,15 +345,9 @@ boolean isCredentialDirectPathCompatible() {
320345
static boolean isOnComputeEngine() {
321346
String osName = System.getProperty("os.name");
322347
if ("Linux".equals(osName)) {
323-
try {
324-
String result =
325-
Files.asCharSource(new File("/sys/class/dmi/id/product_name"), StandardCharsets.UTF_8)
326-
.readFirstLine();
327-
return result.contains(GCE_PRODUCTION_NAME_PRIOR_2016)
328-
|| result.contains(GCE_PRODUCTION_NAME_AFTER_2016);
329-
} catch (IOException ignored) {
330-
return false;
331-
}
348+
// systemProductName will be empty string if not on Compute Engine
349+
return systemProductName.contains(GCE_PRODUCTION_NAME_PRIOR_2016)
350+
|| systemProductName.contains(GCE_PRODUCTION_NAME_AFTER_2016);
332351
}
333352
return false;
334353
}
@@ -370,10 +389,7 @@ private ManagedChannel createSingleChannel() throws IOException {
370389

371390
// Check DirectPath traffic.
372391
boolean useDirectPathXds = false;
373-
if (isDirectPathEnabled()
374-
&& isCredentialDirectPathCompatible()
375-
&& isOnComputeEngine()
376-
&& canUseDirectPathWithUniverseDomain()) {
392+
if (canUseDirectPath()) {
377393
CallCredentials callCreds = MoreCallCredentials.from(credentials);
378394
ChannelCredentials channelCreds =
379395
GoogleDefaultChannelCredentials.newBuilder().callCredentials(callCreds).build();
@@ -446,6 +462,24 @@ && canUseDirectPathWithUniverseDomain()) {
446462
return managedChannel;
447463
}
448464

465+
/**
466+
* Marked as Internal Api and intended for internal use. DirectPath must be enabled via the
467+
* settings and a few other configurations/settings must also be valid for the request to go
468+
* through DirectPath.
469+
*
470+
* <p>Checks: 1. Credentials are compatible 2.Running on Compute Engine 3. Universe Domain is
471+
* configured to for the Google Default Universe
472+
*
473+
* @return if DirectPath is enabled for the client AND if the configurations are valid
474+
*/
475+
@InternalApi
476+
public boolean canUseDirectPath() {
477+
return isDirectPathEnabled()
478+
&& isCredentialDirectPathCompatible()
479+
&& isOnComputeEngine()
480+
&& canUseDirectPathWithUniverseDomain();
481+
}
482+
449483
/** The endpoint to be used for the channel. */
450484
@Override
451485
public String getEndpoint() {
@@ -753,6 +787,12 @@ public Builder setAttemptDirectPathXds() {
753787
return this;
754788
}
755789

790+
@VisibleForTesting
791+
Builder setEnvProvider(EnvironmentProvider envProvider) {
792+
this.envProvider = envProvider;
793+
return this;
794+
}
795+
756796
/**
757797
* Sets a service config for direct path. If direct path is not enabled, the provided service
758798
* config will be ignored.

‎gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java

+196-7
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
*/
3030
package com.google.api.gax.grpc;
3131

32+
import static com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.GCE_PRODUCTION_NAME_AFTER_2016;
3233
import static com.google.common.base.Preconditions.checkArgument;
3334
import static com.google.common.truth.Truth.assertThat;
3435
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -37,13 +38,17 @@
3738
import com.google.api.core.ApiFunction;
3839
import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.Builder;
3940
import com.google.api.gax.rpc.HeaderProvider;
41+
import com.google.api.gax.rpc.TransportChannel;
4042
import com.google.api.gax.rpc.TransportChannelProvider;
43+
import com.google.api.gax.rpc.internal.EnvironmentProvider;
4144
import com.google.api.gax.rpc.mtls.AbstractMtlsTransportChannelTest;
4245
import com.google.api.gax.rpc.mtls.MtlsProvider;
46+
import com.google.auth.Credentials;
4347
import com.google.auth.oauth2.CloudShellCredentials;
4448
import com.google.auth.oauth2.ComputeEngineCredentials;
4549
import com.google.common.collect.ImmutableList;
4650
import com.google.common.collect.ImmutableMap;
51+
import com.google.common.truth.Truth;
4752
import io.grpc.ManagedChannel;
4853
import io.grpc.ManagedChannelBuilder;
4954
import io.grpc.alts.ComputeEngineChannelBuilder;
@@ -57,16 +62,39 @@
5762
import java.util.concurrent.Executor;
5863
import java.util.concurrent.ScheduledExecutorService;
5964
import java.util.concurrent.ScheduledThreadPoolExecutor;
65+
import java.util.concurrent.TimeUnit;
6066
import java.util.logging.Handler;
6167
import java.util.logging.LogRecord;
6268
import java.util.stream.Collectors;
6369
import javax.annotation.Nullable;
70+
import org.junit.jupiter.api.AfterEach;
71+
import org.junit.jupiter.api.BeforeAll;
72+
import org.junit.jupiter.api.BeforeEach;
6473
import org.junit.jupiter.api.Test;
6574
import org.mockito.ArgumentCaptor;
6675
import org.mockito.Mockito;
6776
import org.threeten.bp.Duration;
6877

6978
class InstantiatingGrpcChannelProviderTest extends AbstractMtlsTransportChannelTest {
79+
private static final String DEFAULT_ENDPOINT = "test.googleapis.com:443";
80+
private static String originalOSName;
81+
private ComputeEngineCredentials computeEngineCredentials;
82+
83+
@BeforeAll
84+
public static void setupClass() {
85+
originalOSName = System.getProperty("os.name");
86+
}
87+
88+
@BeforeEach
89+
public void setup() throws IOException {
90+
System.setProperty("os.name", "Linux");
91+
computeEngineCredentials = Mockito.mock(ComputeEngineCredentials.class);
92+
}
93+
94+
@AfterEach
95+
public void cleanup() {
96+
System.setProperty("os.name", originalOSName);
97+
}
7098

7199
@Test
72100
void testEndpoint() {
@@ -300,7 +328,7 @@ void testDirectPathWithGDUEndpoint() {
300328
InstantiatingGrpcChannelProvider.newBuilder()
301329
.setAttemptDirectPath(true)
302330
.setAttemptDirectPathXds()
303-
.setEndpoint("test.googleapis.com:443")
331+
.setEndpoint(DEFAULT_ENDPOINT)
304332
.build();
305333
assertThat(provider.canUseDirectPathWithUniverseDomain()).isTrue();
306334
}
@@ -322,7 +350,7 @@ void testDirectPathXdsEnabled() throws IOException {
322350
InstantiatingGrpcChannelProvider.newBuilder()
323351
.setAttemptDirectPath(true)
324352
.setAttemptDirectPathXds()
325-
.setEndpoint("test.googleapis.com:443")
353+
.setEndpoint(DEFAULT_ENDPOINT)
326354
.build();
327355

328356
assertThat(provider.isDirectPathXdsEnabled()).isTrue();
@@ -552,13 +580,16 @@ void testLogDirectPathMisconfigAttrempDirectPathNotSet() throws Exception {
552580
.setEndpoint("localhost:8080")
553581
.build();
554582

555-
provider.getTransportChannel();
583+
TransportChannel transportChannel = provider.getTransportChannel();
556584

557585
assertThat(logHandler.getAllMessages())
558586
.contains(
559587
"DirectPath is misconfigured. Please set the attemptDirectPath option along with the"
560588
+ " attemptDirectPathXds option.");
561589
InstantiatingGrpcChannelProvider.LOG.removeHandler(logHandler);
590+
591+
transportChannel.close();
592+
transportChannel.awaitTermination(10, TimeUnit.SECONDS);
562593
}
563594

564595
@Test
@@ -584,16 +615,19 @@ void testLogDirectPathMisconfigWrongCredential() throws Exception {
584615
.setAttemptDirectPath(true)
585616
.setHeaderProvider(Mockito.mock(HeaderProvider.class))
586617
.setExecutor(Mockito.mock(Executor.class))
587-
.setEndpoint("test.googleapis.com:443")
618+
.setEndpoint(DEFAULT_ENDPOINT)
588619
.build();
589620

590-
provider.getTransportChannel();
621+
TransportChannel transportChannel = provider.getTransportChannel();
591622

592623
assertThat(logHandler.getAllMessages())
593624
.contains(
594625
"DirectPath is misconfigured. Please make sure the credential is an instance of"
595626
+ " com.google.auth.oauth2.ComputeEngineCredentials .");
596627
InstantiatingGrpcChannelProvider.LOG.removeHandler(logHandler);
628+
629+
transportChannel.close();
630+
transportChannel.awaitTermination(10, TimeUnit.SECONDS);
597631
}
598632

599633
@Test
@@ -607,17 +641,172 @@ void testLogDirectPathMisconfigNotOnGCE() throws Exception {
607641
.setAllowNonDefaultServiceAccount(true)
608642
.setHeaderProvider(Mockito.mock(HeaderProvider.class))
609643
.setExecutor(Mockito.mock(Executor.class))
610-
.setEndpoint("test.googleapis.com:443")
644+
.setEndpoint(DEFAULT_ENDPOINT)
611645
.build();
612646

613-
provider.getTransportChannel();
647+
TransportChannel transportChannel = provider.getTransportChannel();
614648

615649
if (!InstantiatingGrpcChannelProvider.isOnComputeEngine()) {
616650
assertThat(logHandler.getAllMessages())
617651
.contains(
618652
"DirectPath is misconfigured. DirectPath is only available in a GCE environment.");
619653
}
620654
InstantiatingGrpcChannelProvider.LOG.removeHandler(logHandler);
655+
656+
transportChannel.close();
657+
transportChannel.awaitTermination(10, TimeUnit.SECONDS);
658+
}
659+
660+
@Test
661+
public void canUseDirectPath_happyPath() {
662+
EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class);
663+
Mockito.when(
664+
envProvider.getenv(
665+
InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH))
666+
.thenReturn("false");
667+
InstantiatingGrpcChannelProvider.Builder builder =
668+
InstantiatingGrpcChannelProvider.newBuilder()
669+
.setAttemptDirectPath(true)
670+
.setCredentials(computeEngineCredentials)
671+
.setEndpoint(DEFAULT_ENDPOINT)
672+
.setEnvProvider(envProvider);
673+
InstantiatingGrpcChannelProvider provider =
674+
new InstantiatingGrpcChannelProvider(builder, GCE_PRODUCTION_NAME_AFTER_2016);
675+
Truth.assertThat(provider.canUseDirectPath()).isTrue();
676+
}
677+
678+
@Test
679+
public void canUseDirectPath_directPathEnvVarDisabled() {
680+
EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class);
681+
Mockito.when(
682+
envProvider.getenv(
683+
InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH))
684+
.thenReturn("true");
685+
InstantiatingGrpcChannelProvider.Builder builder =
686+
InstantiatingGrpcChannelProvider.newBuilder()
687+
.setAttemptDirectPath(true)
688+
.setCredentials(computeEngineCredentials)
689+
.setEndpoint(DEFAULT_ENDPOINT)
690+
.setEnvProvider(envProvider);
691+
InstantiatingGrpcChannelProvider provider =
692+
new InstantiatingGrpcChannelProvider(builder, GCE_PRODUCTION_NAME_AFTER_2016);
693+
Truth.assertThat(provider.canUseDirectPath()).isFalse();
694+
}
695+
696+
@Test
697+
public void canUseDirectPath_directPathEnvVarNotSet_attemptDirectPathIsTrue() {
698+
InstantiatingGrpcChannelProvider.Builder builder =
699+
InstantiatingGrpcChannelProvider.newBuilder()
700+
.setAttemptDirectPath(true)
701+
.setCredentials(computeEngineCredentials)
702+
.setEndpoint(DEFAULT_ENDPOINT);
703+
InstantiatingGrpcChannelProvider provider =
704+
new InstantiatingGrpcChannelProvider(builder, GCE_PRODUCTION_NAME_AFTER_2016);
705+
Truth.assertThat(provider.canUseDirectPath()).isTrue();
706+
}
707+
708+
@Test
709+
public void canUseDirectPath_directPathEnvVarNotSet_attemptDirectPathIsFalse() {
710+
InstantiatingGrpcChannelProvider.Builder builder =
711+
InstantiatingGrpcChannelProvider.newBuilder()
712+
.setAttemptDirectPath(false)
713+
.setCredentials(computeEngineCredentials)
714+
.setEndpoint(DEFAULT_ENDPOINT);
715+
InstantiatingGrpcChannelProvider provider =
716+
new InstantiatingGrpcChannelProvider(builder, GCE_PRODUCTION_NAME_AFTER_2016);
717+
Truth.assertThat(provider.canUseDirectPath()).isFalse();
718+
}
719+
720+
@Test
721+
public void canUseDirectPath_nonComputeCredentials() {
722+
Credentials credentials = Mockito.mock(Credentials.class);
723+
EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class);
724+
Mockito.when(
725+
envProvider.getenv(
726+
InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH))
727+
.thenReturn("false");
728+
InstantiatingGrpcChannelProvider.Builder builder =
729+
InstantiatingGrpcChannelProvider.newBuilder()
730+
.setAttemptDirectPath(true)
731+
.setCredentials(credentials)
732+
.setEndpoint(DEFAULT_ENDPOINT)
733+
.setEnvProvider(envProvider);
734+
InstantiatingGrpcChannelProvider provider =
735+
new InstantiatingGrpcChannelProvider(builder, GCE_PRODUCTION_NAME_AFTER_2016);
736+
Truth.assertThat(provider.canUseDirectPath()).isFalse();
737+
}
738+
739+
@Test
740+
public void canUseDirectPath_isNotOnComputeEngine_invalidOsNameSystemProperty() {
741+
System.setProperty("os.name", "Not Linux");
742+
EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class);
743+
Mockito.when(
744+
envProvider.getenv(
745+
InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH))
746+
.thenReturn("false");
747+
InstantiatingGrpcChannelProvider.Builder builder =
748+
InstantiatingGrpcChannelProvider.newBuilder()
749+
.setAttemptDirectPath(true)
750+
.setCredentials(computeEngineCredentials)
751+
.setEndpoint(DEFAULT_ENDPOINT)
752+
.setEnvProvider(envProvider);
753+
InstantiatingGrpcChannelProvider provider =
754+
new InstantiatingGrpcChannelProvider(builder, GCE_PRODUCTION_NAME_AFTER_2016);
755+
Truth.assertThat(provider.canUseDirectPath()).isFalse();
756+
}
757+
758+
@Test
759+
public void canUseDirectPath_isNotOnComputeEngine_invalidSystemProductName() {
760+
EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class);
761+
Mockito.when(
762+
envProvider.getenv(
763+
InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH))
764+
.thenReturn("false");
765+
InstantiatingGrpcChannelProvider.Builder builder =
766+
InstantiatingGrpcChannelProvider.newBuilder()
767+
.setAttemptDirectPath(true)
768+
.setCredentials(computeEngineCredentials)
769+
.setEndpoint(DEFAULT_ENDPOINT)
770+
.setEnvProvider(envProvider);
771+
InstantiatingGrpcChannelProvider provider =
772+
new InstantiatingGrpcChannelProvider(builder, "testing");
773+
Truth.assertThat(provider.canUseDirectPath()).isFalse();
774+
}
775+
776+
@Test
777+
public void canUseDirectPath_isNotOnComputeEngine_unableToGetSystemProductName() {
778+
EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class);
779+
Mockito.when(
780+
envProvider.getenv(
781+
InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH))
782+
.thenReturn("false");
783+
InstantiatingGrpcChannelProvider.Builder builder =
784+
InstantiatingGrpcChannelProvider.newBuilder()
785+
.setAttemptDirectPath(true)
786+
.setCredentials(computeEngineCredentials)
787+
.setEndpoint(DEFAULT_ENDPOINT)
788+
.setEnvProvider(envProvider);
789+
InstantiatingGrpcChannelProvider provider = new InstantiatingGrpcChannelProvider(builder, "");
790+
Truth.assertThat(provider.canUseDirectPath()).isFalse();
791+
}
792+
793+
@Test
794+
public void canUseDirectPath_nonGDUUniverseDomain() {
795+
EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class);
796+
Mockito.when(
797+
envProvider.getenv(
798+
InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH))
799+
.thenReturn("false");
800+
String nonGDUEndpoint = "test.random.com:443";
801+
InstantiatingGrpcChannelProvider.Builder builder =
802+
InstantiatingGrpcChannelProvider.newBuilder()
803+
.setAttemptDirectPath(true)
804+
.setCredentials(computeEngineCredentials)
805+
.setEndpoint(nonGDUEndpoint)
806+
.setEnvProvider(envProvider);
807+
InstantiatingGrpcChannelProvider provider =
808+
new InstantiatingGrpcChannelProvider(builder, GCE_PRODUCTION_NAME_AFTER_2016);
809+
Truth.assertThat(provider.canUseDirectPath()).isFalse();
621810
}
622811

623812
private static class FakeLogHandler extends Handler {

‎gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsTracer.java

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public class MetricsTracer implements ApiTracer {
6161
"Operation has already been completed";
6262
private Stopwatch attemptTimer;
6363
private final Stopwatch operationTimer = Stopwatch.createStarted();
64+
// These are RPC specific attributes and pertain to a specific API Trace
6465
private final Map<String, String> attributes = new HashMap<>();
6566
private final MetricsRecorder metricsRecorder;
6667
private final AtomicBoolean operationFinished;

‎gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsTracerFactory.java

+20-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131

3232
import com.google.api.core.BetaApi;
3333
import com.google.api.core.InternalApi;
34+
import com.google.common.collect.ImmutableMap;
35+
import java.util.Map;
3436

3537
/**
3638
* A {@link ApiTracerFactory} to build instances of {@link MetricsTracer}.
@@ -45,13 +47,29 @@
4547
public class MetricsTracerFactory implements ApiTracerFactory {
4648
protected MetricsRecorder metricsRecorder;
4749

50+
/** Mapping of client attributes that are set for every MetricsTracer */
51+
private final Map<String, String> attributes;
52+
53+
/** Creates a MetricsTracerFactory with no additional client level attributes. */
4854
public MetricsTracerFactory(MetricsRecorder metricsRecorder) {
55+
this(metricsRecorder, ImmutableMap.of());
56+
}
57+
58+
/**
59+
* Pass in a Map of client level attributes which will be added to every single MetricsTracer
60+
* created from the ApiTracerFactory.
61+
*/
62+
public MetricsTracerFactory(MetricsRecorder metricsRecorder, Map<String, String> attributes) {
4963
this.metricsRecorder = metricsRecorder;
64+
this.attributes = ImmutableMap.copyOf(attributes);
5065
}
5166

5267
@Override
5368
public ApiTracer newTracer(ApiTracer parent, SpanName spanName, OperationType operationType) {
54-
return new MetricsTracer(
55-
MethodName.of(spanName.getClientName(), spanName.getMethodName()), metricsRecorder);
69+
MetricsTracer metricsTracer =
70+
new MetricsTracer(
71+
MethodName.of(spanName.getClientName(), spanName.getMethodName()), metricsRecorder);
72+
attributes.forEach(metricsTracer::addAttributes);
73+
return metricsTracer;
5674
}
5775
}

‎gax-java/gax/src/test/java/com/google/api/gax/tracing/MetricsTracerFactoryTest.java

+29-11
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,16 @@
3333
import static org.mockito.Mockito.when;
3434

3535
import com.google.api.gax.tracing.ApiTracerFactory.OperationType;
36+
import com.google.common.collect.ImmutableMap;
3637
import com.google.common.truth.Truth;
38+
import java.util.Map;
3739
import org.junit.jupiter.api.BeforeEach;
3840
import org.junit.jupiter.api.Test;
3941
import org.mockito.Mock;
4042

4143
class MetricsTracerFactoryTest {
44+
private static final int DEFAULT_ATTRIBUTES_COUNT = 2;
45+
4246
@Mock private MetricsRecorder metricsRecorder;
4347
@Mock private ApiTracer parent;
4448
private SpanName spanName;
@@ -60,22 +64,36 @@ void testNewTracer_notNull() {
6064
ApiTracer apiTracer = metricsTracerFactory.newTracer(parent, spanName, OperationType.Unary);
6165

6266
// Assert that the apiTracer created has expected type and not null
63-
Truth.assertThat(apiTracer).isInstanceOf(MetricsTracer.class);
6467
Truth.assertThat(apiTracer).isNotNull();
68+
Truth.assertThat(apiTracer).isInstanceOf(MetricsTracer.class);
6569
}
6670

6771
@Test
68-
void testNewTracer_HasCorrectParameters() {
69-
70-
// Call the newTracer method
71-
ApiTracer apiTracer = metricsTracerFactory.newTracer(parent, spanName, OperationType.Unary);
72+
void testNewTracer_hasCorrectNumberAttributes_hasDefaultAttributes() {
73+
MetricsTracer metricsTracer =
74+
(MetricsTracer) metricsTracerFactory.newTracer(parent, spanName, OperationType.Unary);
75+
Map<String, String> attributes = metricsTracer.getAttributes();
76+
Truth.assertThat(attributes.size()).isEqualTo(DEFAULT_ATTRIBUTES_COUNT);
77+
Truth.assertThat(attributes.get(MetricsTracer.METHOD_NAME_ATTRIBUTE))
78+
.isEqualTo("testService.testMethod");
79+
Truth.assertThat(attributes.get(MetricsTracer.LANGUAGE_ATTRIBUTE))
80+
.isEqualTo(MetricsTracer.DEFAULT_LANGUAGE);
81+
}
7282

73-
// Assert that the apiTracer created has expected type and not null
74-
Truth.assertThat(apiTracer).isInstanceOf(MetricsTracer.class);
75-
Truth.assertThat(apiTracer).isNotNull();
83+
@Test
84+
void testClientAttributes_additionalClientAttributes() {
85+
Map<String, String> clientAttributes =
86+
ImmutableMap.of("attribute1", "value1", "attribute2", "value2");
87+
MetricsTracerFactory metricsTracerFactory =
88+
new MetricsTracerFactory(metricsRecorder, clientAttributes);
7689

77-
MetricsTracer metricsTracer = (MetricsTracer) apiTracer;
78-
Truth.assertThat(metricsTracer.getAttributes().get("method_name"))
79-
.isEqualTo("testService.testMethod");
90+
MetricsTracer metricsTracer =
91+
(MetricsTracer) metricsTracerFactory.newTracer(parent, spanName, OperationType.Unary);
92+
Map<String, String> attributes = metricsTracer.getAttributes();
93+
Truth.assertThat(attributes.size())
94+
.isEqualTo(DEFAULT_ATTRIBUTES_COUNT + clientAttributes.size());
95+
// Default attributes already tested above
96+
Truth.assertThat(attributes.containsKey("attribute1")).isTrue();
97+
Truth.assertThat(attributes.containsKey("attribute2")).isTrue();
8098
}
8199
}

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

+143-66
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.google.api.client.http.javanet.NetHttpTransport;
3636
import com.google.api.core.ApiFuture;
3737
import com.google.api.gax.core.NoCredentialsProvider;
38+
import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider;
3839
import com.google.api.gax.retrying.RetrySettings;
3940
import com.google.api.gax.rpc.InvalidArgumentException;
4041
import com.google.api.gax.rpc.StatusCode.Code;
@@ -69,7 +70,9 @@
6970
import io.opentelemetry.sdk.metrics.data.MetricData;
7071
import io.opentelemetry.sdk.metrics.data.PointData;
7172
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
73+
import java.io.IOException;
7274
import java.util.ArrayList;
75+
import java.util.HashMap;
7376
import java.util.List;
7477
import java.util.Map;
7578
import java.util.Optional;
@@ -100,7 +103,7 @@ class ITOtelMetrics {
100103
private static final String OPERATION_COUNT = SERVICE_NAME + "/operation_count";
101104
private static final String ATTEMPT_LATENCY = SERVICE_NAME + "/attempt_latency";
102105
private static final String OPERATION_LATENCY = SERVICE_NAME + "/operation_latency";
103-
private static final int NUM_METRICS = 4;
106+
private static final int NUM_DEFAULT_METRICS = 4;
104107
private static final int NUM_COLLECTION_FLUSH_ATTEMPTS = 10;
105108
private InMemoryMetricReader inMemoryMetricReader;
106109
private EchoClient grpcClient;
@@ -272,16 +275,22 @@ private void verifyStatusAttribute(
272275
}
273276
}
274277

278+
/** Uses the default InMemoryMetricReader configured for showcase tests. */
279+
private List<MetricData> getMetricDataList() throws InterruptedException {
280+
return getMetricDataList(inMemoryMetricReader);
281+
}
282+
275283
/**
276-
* Attempts to retrieve the metrics from the InMemoryMetricsReader. Sleep every second for at most
277-
* 10s to try and retrieve all the metrics available. If it is unable to retrieve all the metrics,
278-
* fail the test.
284+
* Attempts to retrieve the metrics from a custom InMemoryMetricsReader. Sleep every second for at
285+
* most 10s to try and retrieve all the metrics available. If it is unable to retrieve all the
286+
* metrics, fail the test.
279287
*/
280-
private List<MetricData> getMetricDataList() throws InterruptedException {
288+
private List<MetricData> getMetricDataList(InMemoryMetricReader metricReader)
289+
throws InterruptedException {
281290
for (int i = 0; i < NUM_COLLECTION_FLUSH_ATTEMPTS; i++) {
282291
Thread.sleep(1000L);
283-
List<MetricData> metricData = new ArrayList<>(inMemoryMetricReader.collectAllMetrics());
284-
if (metricData.size() == NUM_METRICS) {
292+
List<MetricData> metricData = new ArrayList<>(metricReader.collectAllMetrics());
293+
if (metricData.size() == NUM_DEFAULT_METRICS) {
285294
return metricData;
286295
}
287296
}
@@ -296,19 +305,19 @@ void testGrpc_operationSucceeded_recordsMetrics() throws InterruptedException {
296305
EchoRequest.newBuilder().setContent("test_grpc_operation_succeeded").build();
297306
grpcClient.echo(echoRequest);
298307

299-
List<MetricData> metricDataList = getMetricDataList();
300-
verifyPointDataSum(metricDataList, attemptCount);
308+
List<MetricData> actualMetricDataList = getMetricDataList();
309+
verifyPointDataSum(actualMetricDataList, attemptCount);
301310

302-
Map<String, String> attributeMapping =
311+
Map<String, String> expectedAttributes =
303312
ImmutableMap.of(
304313
MetricsTracer.METHOD_NAME_ATTRIBUTE,
305314
"Echo.Echo",
306315
MetricsTracer.LANGUAGE_ATTRIBUTE,
307316
MetricsTracer.DEFAULT_LANGUAGE);
308-
verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
317+
verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
309318

310319
List<StatusCount> statusCountList = ImmutableList.of(new StatusCount(Code.OK));
311-
verifyStatusAttribute(metricDataList, statusCountList);
320+
verifyStatusAttribute(actualMetricDataList, statusCountList);
312321
}
313322

314323
@Disabled("https://github.com/googleapis/sdk-platform-java/issues/2503")
@@ -319,19 +328,19 @@ void testHttpJson_operationSucceeded_recordsMetrics() throws InterruptedExceptio
319328
EchoRequest.newBuilder().setContent("test_http_operation_succeeded").build();
320329
httpClient.echo(echoRequest);
321330

322-
List<MetricData> metricDataList = getMetricDataList();
323-
verifyPointDataSum(metricDataList, attemptCount);
331+
List<MetricData> actualMetricDataList = getMetricDataList();
332+
verifyPointDataSum(actualMetricDataList, attemptCount);
324333

325-
Map<String, String> attributeMapping =
334+
Map<String, String> expectedAttributes =
326335
ImmutableMap.of(
327336
MetricsTracer.METHOD_NAME_ATTRIBUTE,
328337
"google.showcase.v1beta1.Echo/Echo",
329338
MetricsTracer.LANGUAGE_ATTRIBUTE,
330339
MetricsTracer.DEFAULT_LANGUAGE);
331-
verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
340+
verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
332341

333342
List<StatusCount> statusCountList = ImmutableList.of(new StatusCount(Code.OK));
334-
verifyStatusAttribute(metricDataList, statusCountList);
343+
verifyStatusAttribute(actualMetricDataList, statusCountList);
335344
}
336345

337346
@Test
@@ -349,19 +358,19 @@ void testGrpc_operationCancelled_recordsMetrics() throws Exception {
349358
Thread.sleep(1000);
350359
blockResponseApiFuture.cancel(true);
351360

352-
List<MetricData> metricDataList = getMetricDataList();
353-
verifyPointDataSum(metricDataList, attemptCount);
361+
List<MetricData> actualMetricDataList = getMetricDataList();
362+
verifyPointDataSum(actualMetricDataList, attemptCount);
354363

355-
Map<String, String> attributeMapping =
364+
Map<String, String> expectedAttributes =
356365
ImmutableMap.of(
357366
MetricsTracer.METHOD_NAME_ATTRIBUTE,
358367
"Echo.Block",
359368
MetricsTracer.LANGUAGE_ATTRIBUTE,
360369
MetricsTracer.DEFAULT_LANGUAGE);
361-
verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
370+
verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
362371

363372
List<StatusCount> statusCountList = ImmutableList.of(new StatusCount(Code.CANCELLED));
364-
verifyStatusAttribute(metricDataList, statusCountList);
373+
verifyStatusAttribute(actualMetricDataList, statusCountList);
365374
}
366375

367376
@Disabled("https://github.com/googleapis/sdk-platform-java/issues/2503")
@@ -377,19 +386,19 @@ void testHttpJson_operationCancelled_recordsMetrics() throws Exception {
377386
Thread.sleep(1000);
378387
blockResponseApiFuture.cancel(true);
379388

380-
List<MetricData> metricDataList = getMetricDataList();
381-
verifyPointDataSum(metricDataList, attemptCount);
389+
List<MetricData> actualMetricDataList = getMetricDataList();
390+
verifyPointDataSum(actualMetricDataList, attemptCount);
382391

383-
Map<String, String> attributeMapping =
392+
Map<String, String> expectedAttributes =
384393
ImmutableMap.of(
385394
MetricsTracer.METHOD_NAME_ATTRIBUTE,
386395
"google.showcase.v1beta1.Echo/Block",
387396
MetricsTracer.LANGUAGE_ATTRIBUTE,
388397
MetricsTracer.DEFAULT_LANGUAGE);
389-
verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
398+
verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
390399

391400
List<StatusCount> statusCountList = ImmutableList.of(new StatusCount(Code.CANCELLED));
392-
verifyStatusAttribute(metricDataList, statusCountList);
401+
verifyStatusAttribute(actualMetricDataList, statusCountList);
393402
}
394403

395404
@Test
@@ -406,19 +415,19 @@ void testGrpc_operationFailed_recordsMetrics() throws InterruptedException {
406415
ApiFuture<BlockResponse> blockResponseApiFuture = blockCallable.futureCall(blockRequest);
407416
assertThrows(ExecutionException.class, blockResponseApiFuture::get);
408417

409-
List<MetricData> metricDataList = getMetricDataList();
410-
verifyPointDataSum(metricDataList, attemptCount);
418+
List<MetricData> actualMetricDataList = getMetricDataList();
419+
verifyPointDataSum(actualMetricDataList, attemptCount);
411420

412-
Map<String, String> attributeMapping =
421+
Map<String, String> expectedAttributes =
413422
ImmutableMap.of(
414423
MetricsTracer.METHOD_NAME_ATTRIBUTE,
415424
"Echo.Block",
416425
MetricsTracer.LANGUAGE_ATTRIBUTE,
417426
MetricsTracer.DEFAULT_LANGUAGE);
418-
verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
427+
verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
419428

420429
List<StatusCount> statusCountList = ImmutableList.of(new StatusCount(statusCode));
421-
verifyStatusAttribute(metricDataList, statusCountList);
430+
verifyStatusAttribute(actualMetricDataList, statusCountList);
422431
}
423432

424433
@Disabled("https://github.com/googleapis/sdk-platform-java/issues/2503")
@@ -436,19 +445,19 @@ void testHttpJson_operationFailed_recordsMetrics() throws InterruptedException {
436445
ApiFuture<BlockResponse> blockResponseApiFuture = blockCallable.futureCall(blockRequest);
437446
assertThrows(ExecutionException.class, blockResponseApiFuture::get);
438447

439-
List<MetricData> metricDataList = getMetricDataList();
440-
verifyPointDataSum(metricDataList, attemptCount);
448+
List<MetricData> actualMetricDataList = getMetricDataList();
449+
verifyPointDataSum(actualMetricDataList, attemptCount);
441450

442-
Map<String, String> attributeMapping =
451+
Map<String, String> expectedAttributes =
443452
ImmutableMap.of(
444453
MetricsTracer.METHOD_NAME_ATTRIBUTE,
445454
"google.showcase.v1beta1.Echo/Block",
446455
MetricsTracer.LANGUAGE_ATTRIBUTE,
447456
MetricsTracer.DEFAULT_LANGUAGE);
448-
verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
457+
verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
449458

450459
List<StatusCount> statusCountList = ImmutableList.of(new StatusCount(statusCode));
451-
verifyStatusAttribute(metricDataList, statusCountList);
460+
verifyStatusAttribute(actualMetricDataList, statusCountList);
452461
}
453462

454463
@Test
@@ -499,19 +508,19 @@ void testGrpc_attemptFailedRetriesExhausted_recordsMetrics() throws Exception {
499508

500509
assertThrows(UnavailableException.class, () -> grpcClient.echo(echoRequest));
501510

502-
List<MetricData> metricDataList = getMetricDataList();
503-
verifyPointDataSum(metricDataList, attemptCount);
511+
List<MetricData> actualMetricDataList = getMetricDataList();
512+
verifyPointDataSum(actualMetricDataList, attemptCount);
504513

505-
Map<String, String> attributeMapping =
514+
Map<String, String> expectedAttributes =
506515
ImmutableMap.of(
507516
MetricsTracer.METHOD_NAME_ATTRIBUTE,
508517
"Echo.Echo",
509518
MetricsTracer.LANGUAGE_ATTRIBUTE,
510519
MetricsTracer.DEFAULT_LANGUAGE);
511-
verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
520+
verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
512521

513522
List<StatusCount> statusCountList = ImmutableList.of(new StatusCount(statusCode, 3));
514-
verifyStatusAttribute(metricDataList, statusCountList);
523+
verifyStatusAttribute(actualMetricDataList, statusCountList);
515524

516525
grpcClient.close();
517526
grpcClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS);
@@ -567,19 +576,19 @@ void testHttpJson_attemptFailedRetriesExhausted_recordsMetrics() throws Exceptio
567576

568577
assertThrows(UnavailableException.class, () -> httpClient.echo(echoRequest));
569578

570-
List<MetricData> metricDataList = getMetricDataList();
571-
verifyPointDataSum(metricDataList, attemptCount);
579+
List<MetricData> actualMetricDataList = getMetricDataList();
580+
verifyPointDataSum(actualMetricDataList, attemptCount);
572581

573-
Map<String, String> attributeMapping =
582+
Map<String, String> expectedAttributes =
574583
ImmutableMap.of(
575584
MetricsTracer.METHOD_NAME_ATTRIBUTE,
576585
"google.showcase.v1beta1.Echo/Echo",
577586
MetricsTracer.LANGUAGE_ATTRIBUTE,
578587
MetricsTracer.DEFAULT_LANGUAGE);
579-
verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
588+
verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
580589

581590
List<StatusCount> statusCountList = ImmutableList.of(new StatusCount(statusCode, 3));
582-
verifyStatusAttribute(metricDataList, statusCountList);
591+
verifyStatusAttribute(actualMetricDataList, statusCountList);
583592

584593
httpClient.close();
585594
httpClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS);
@@ -597,19 +606,19 @@ void testGrpc_attemptPermanentFailure_recordsMetrics() throws InterruptedExcepti
597606

598607
assertThrows(InvalidArgumentException.class, () -> grpcClient.block(blockRequest));
599608

600-
List<MetricData> metricDataList = getMetricDataList();
601-
verifyPointDataSum(metricDataList, attemptCount);
609+
List<MetricData> actualMetricDataList = getMetricDataList();
610+
verifyPointDataSum(actualMetricDataList, attemptCount);
602611

603-
Map<String, String> attributeMapping =
612+
Map<String, String> expectedAttributes =
604613
ImmutableMap.of(
605614
MetricsTracer.METHOD_NAME_ATTRIBUTE,
606615
"Echo.Block",
607616
MetricsTracer.LANGUAGE_ATTRIBUTE,
608617
MetricsTracer.DEFAULT_LANGUAGE);
609-
verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
618+
verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
610619

611620
List<StatusCount> statusCountList = ImmutableList.of(new StatusCount(statusCode));
612-
verifyStatusAttribute(metricDataList, statusCountList);
621+
verifyStatusAttribute(actualMetricDataList, statusCountList);
613622
}
614623

615624
@Disabled("https://github.com/googleapis/sdk-platform-java/issues/2503")
@@ -625,19 +634,19 @@ void testHttpJson_attemptPermanentFailure_recordsMetrics() throws InterruptedExc
625634

626635
assertThrows(InvalidArgumentException.class, () -> httpClient.block(blockRequest));
627636

628-
List<MetricData> metricDataList = getMetricDataList();
629-
verifyPointDataSum(metricDataList, attemptCount);
637+
List<MetricData> actualMetricDataList = getMetricDataList();
638+
verifyPointDataSum(actualMetricDataList, attemptCount);
630639

631-
Map<String, String> attributeMapping =
640+
Map<String, String> expectedAttributes =
632641
ImmutableMap.of(
633642
MetricsTracer.METHOD_NAME_ATTRIBUTE,
634643
"google.showcase.v1beta1.Echo/Block",
635644
MetricsTracer.LANGUAGE_ATTRIBUTE,
636645
MetricsTracer.DEFAULT_LANGUAGE);
637-
verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
646+
verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
638647

639648
List<StatusCount> statusCountList = ImmutableList.of(new StatusCount(statusCode));
640-
verifyStatusAttribute(metricDataList, statusCountList);
649+
verifyStatusAttribute(actualMetricDataList, statusCountList);
641650
}
642651

643652
@Test
@@ -694,20 +703,20 @@ void testGrpc_multipleFailedAttempts_successfulOperation() throws Exception {
694703

695704
grpcClient.block(blockRequest);
696705

697-
List<MetricData> metricDataList = getMetricDataList();
698-
verifyPointDataSum(metricDataList, attemptCount);
706+
List<MetricData> actualMetricDataList = getMetricDataList();
707+
verifyPointDataSum(actualMetricDataList, attemptCount);
699708

700-
Map<String, String> attributeMapping =
709+
Map<String, String> expectedAttributes =
701710
ImmutableMap.of(
702711
MetricsTracer.METHOD_NAME_ATTRIBUTE,
703712
"Echo.Block",
704713
MetricsTracer.LANGUAGE_ATTRIBUTE,
705714
MetricsTracer.DEFAULT_LANGUAGE);
706-
verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
715+
verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
707716

708717
List<StatusCount> statusCountList =
709718
ImmutableList.of(new StatusCount(Code.DEADLINE_EXCEEDED, 2), new StatusCount(Code.OK));
710-
verifyStatusAttribute(metricDataList, statusCountList);
719+
verifyStatusAttribute(actualMetricDataList, statusCountList);
711720

712721
grpcClient.close();
713722
grpcClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS);
@@ -764,18 +773,86 @@ void testHttpJson_multipleFailedAttempts_successfulOperation() throws Exception
764773

765774
grpcClient.block(blockRequest);
766775

767-
List<MetricData> metricDataList = getMetricDataList();
768-
verifyPointDataSum(metricDataList, attemptCount);
776+
List<MetricData> actualMetricDataList = getMetricDataList();
777+
verifyPointDataSum(actualMetricDataList, attemptCount);
769778

770-
Map<String, String> attributeMapping =
779+
Map<String, String> expectedAttributes =
771780
ImmutableMap.of(
772781
MetricsTracer.METHOD_NAME_ATTRIBUTE,
773782
"google.showcase.v1beta1.Echo/Block",
774783
MetricsTracer.LANGUAGE_ATTRIBUTE,
775784
MetricsTracer.DEFAULT_LANGUAGE);
776-
verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
785+
verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
777786

778787
httpClient.close();
779788
httpClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS);
780789
}
790+
791+
@Test
792+
void recordsCustomAttributes() throws InterruptedException, IOException {
793+
InstantiatingGrpcChannelProvider channelProvider =
794+
EchoSettings.defaultGrpcTransportProviderBuilder()
795+
.setChannelConfigurator(ManagedChannelBuilder::usePlaintext)
796+
.setAttemptDirectPathXds()
797+
.build();
798+
799+
// Add custom attributes to be added as client level attributes
800+
Map<String, String> customAttributes = new HashMap<>();
801+
String directpathEnabled = "directpath_enabled";
802+
customAttributes.put(directpathEnabled, String.valueOf(channelProvider.canUseDirectPath()));
803+
String directpathXdsEnabled = "directpathxds_enabled";
804+
customAttributes.put(
805+
directpathXdsEnabled, String.valueOf(channelProvider.isDirectPathXdsEnabled()));
806+
String randomAttributeKey1 = "testing";
807+
String randomAttributeValue1 = "showcase";
808+
String randomAttributeKey2 = "hello";
809+
String randomAttributeValue2 = "world";
810+
customAttributes.put(randomAttributeKey1, randomAttributeValue1);
811+
customAttributes.put(randomAttributeKey2, randomAttributeValue2);
812+
813+
InMemoryMetricReader inMemoryMetricReader = InMemoryMetricReader.create();
814+
OpenTelemetryMetricsRecorder otelMetricsRecorder =
815+
createOtelMetricsRecorder(inMemoryMetricReader);
816+
MetricsTracerFactory metricsTracerFactory =
817+
new MetricsTracerFactory(otelMetricsRecorder, customAttributes);
818+
819+
EchoSettings grpcEchoSettings =
820+
EchoSettings.newBuilder()
821+
.setCredentialsProvider(NoCredentialsProvider.create())
822+
.setTransportChannelProvider(channelProvider)
823+
.setEndpoint(TestClientInitializer.DEFAULT_GRPC_ENDPOINT)
824+
.build();
825+
826+
EchoStubSettings echoStubSettings =
827+
(EchoStubSettings)
828+
grpcEchoSettings
829+
.getStubSettings()
830+
.toBuilder()
831+
.setTracerFactory(metricsTracerFactory)
832+
.build();
833+
EchoStub stub = echoStubSettings.createStub();
834+
EchoClient grpcClient = EchoClient.create(stub);
835+
836+
EchoRequest echoRequest = EchoRequest.newBuilder().setContent("content").build();
837+
grpcClient.echo(echoRequest);
838+
839+
List<MetricData> actualMetricDataList = getMetricDataList(inMemoryMetricReader);
840+
Map<String, String> expectedAttributes =
841+
ImmutableMap.of(
842+
MetricsTracer.METHOD_NAME_ATTRIBUTE,
843+
"Echo.Echo",
844+
MetricsTracer.LANGUAGE_ATTRIBUTE,
845+
MetricsTracer.DEFAULT_LANGUAGE,
846+
directpathEnabled,
847+
"false",
848+
directpathXdsEnabled,
849+
"true",
850+
randomAttributeKey1,
851+
randomAttributeValue1,
852+
randomAttributeKey2,
853+
randomAttributeValue2);
854+
verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
855+
856+
inMemoryMetricReader.close();
857+
}
781858
}

0 commit comments

Comments
 (0)
Please sign in to comment.