diff --git a/gax-java/gax-grpc/pom.xml b/gax-java/gax-grpc/pom.xml
index b25e708814..1bdca58764 100644
--- a/gax-java/gax-grpc/pom.xml
+++ b/gax-java/gax-grpc/pom.xml
@@ -128,6 +128,16 @@
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ --add-opens java.base/java.util=ALL-UNNAMED
+ --add-opens java.base/java.lang=ALL-UNNAMED
+
+
+
\ No newline at end of file
diff --git a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java
index 5969ed4693..cd3eed190a 100644
--- a/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java
+++ b/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java
@@ -32,7 +32,6 @@
import com.google.api.core.ApiFunction;
import com.google.api.core.BetaApi;
import com.google.api.core.InternalApi;
-import com.google.api.core.InternalExtensionOnly;
import com.google.api.gax.core.ExecutorProvider;
import com.google.api.gax.rpc.FixedHeaderProvider;
import com.google.api.gax.rpc.HeaderProvider;
@@ -82,13 +81,24 @@
*
The client lib header and generator header values are used to form a value that goes into the
* http header of requests to the service.
*/
-@InternalExtensionOnly
public final class InstantiatingGrpcChannelProvider implements TransportChannelProvider {
+
+ static String systemProductName;
+
+ static {
+ try {
+ systemProductName =
+ Files.asCharSource(new File("/sys/class/dmi/id/product_name"), StandardCharsets.UTF_8)
+ .readFirstLine();
+ } catch (IOException e) {
+ systemProductName = null;
+ }
+ }
+
@VisibleForTesting
static final Logger LOG = Logger.getLogger(InstantiatingGrpcChannelProvider.class.getName());
- private static final String DIRECT_PATH_ENV_DISABLE_DIRECT_PATH =
- "GOOGLE_CLOUD_DISABLE_DIRECT_PATH";
+ static final String DIRECT_PATH_ENV_DISABLE_DIRECT_PATH = "GOOGLE_CLOUD_DISABLE_DIRECT_PATH";
private static final String DIRECT_PATH_ENV_ENABLE_XDS = "GOOGLE_CLOUD_ENABLE_DIRECT_PATH_XDS";
static final long DIRECT_PATH_KEEP_ALIVE_TIME_SECONDS = 3600;
static final long DIRECT_PATH_KEEP_ALIVE_TIMEOUT_SECONDS = 20;
@@ -145,6 +155,7 @@ private InstantiatingGrpcChannelProvider(Builder builder) {
builder.directPathServiceConfig == null
? getDefaultDirectPathServiceConfig()
: builder.directPathServiceConfig;
+ systemProductName = builder.systemProductName;
}
/**
@@ -319,16 +330,11 @@ boolean isCredentialDirectPathCompatible() {
@VisibleForTesting
static boolean isOnComputeEngine() {
String osName = System.getProperty("os.name");
- if ("Linux".equals(osName)) {
- try {
- String result =
- Files.asCharSource(new File("/sys/class/dmi/id/product_name"), StandardCharsets.UTF_8)
- .readFirstLine();
- return result.contains(GCE_PRODUCTION_NAME_PRIOR_2016)
- || result.contains(GCE_PRODUCTION_NAME_AFTER_2016);
- } catch (IOException ignored) {
- return false;
- }
+ // systemProductName null check is in case there is an IOException
+ // IOException will set the systemProductName to null and should return false
+ if ("Linux".equals(osName) && systemProductName != null) {
+ return systemProductName.contains(GCE_PRODUCTION_NAME_PRIOR_2016)
+ || systemProductName.contains(GCE_PRODUCTION_NAME_AFTER_2016);
}
return false;
}
@@ -370,10 +376,7 @@ private ManagedChannel createSingleChannel() throws IOException {
// Check DirectPath traffic.
boolean useDirectPathXds = false;
- if (isDirectPathEnabled()
- && isCredentialDirectPathCompatible()
- && isOnComputeEngine()
- && canUseDirectPathWithUniverseDomain()) {
+ if (canUseDirectPath()) {
CallCredentials callCreds = MoreCallCredentials.from(credentials);
ChannelCredentials channelCreds =
GoogleDefaultChannelCredentials.newBuilder().callCredentials(callCreds).build();
@@ -446,6 +449,24 @@ && canUseDirectPathWithUniverseDomain()) {
return managedChannel;
}
+ /**
+ * Marked as Internal Api and intended for internal use. DirectPath must be enabled via the
+ * settings and a few other configurations/settings must also be valid for the request to go
+ * through DirectPath.
+ *
+ *
Checks: 1. Credentials are compatible 2.Running on Compute Engine 3. Universe Domain is
+ * configured to for the Google Default Universe
+ *
+ * @return if DirectPath is enabled for the client AND if the configurations are valid
+ */
+ @InternalApi
+ public boolean canUseDirectPath() {
+ return isDirectPathEnabled()
+ && isCredentialDirectPathCompatible()
+ && isOnComputeEngine()
+ && canUseDirectPathWithUniverseDomain();
+ }
+
/** The endpoint to be used for the channel. */
@Override
public String getEndpoint() {
@@ -513,6 +534,7 @@ public static final class Builder {
@Nullable private Boolean attemptDirectPathXds;
@Nullable private Boolean allowNonDefaultServiceAccount;
@Nullable private ImmutableMap directPathServiceConfig;
+ @Nullable private String systemProductName;
private Builder() {
processorCount = Runtime.getRuntime().availableProcessors();
@@ -541,6 +563,7 @@ private Builder(InstantiatingGrpcChannelProvider provider) {
this.allowNonDefaultServiceAccount = provider.allowNonDefaultServiceAccount;
this.directPathServiceConfig = provider.directPathServiceConfig;
this.mtlsProvider = provider.mtlsProvider;
+ this.systemProductName = null;
}
/**
@@ -753,6 +776,12 @@ public Builder setAttemptDirectPathXds() {
return this;
}
+ @VisibleForTesting
+ Builder setSystemProductName(String systemProductName) {
+ this.systemProductName = systemProductName;
+ return this;
+ }
+
/**
* Sets a service config for direct path. If direct path is not enabled, the provided service
* config will be ignored.
diff --git a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java
index 2647ac6d13..0daddd6629 100644
--- a/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java
+++ b/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java
@@ -29,6 +29,7 @@
*/
package com.google.api.gax.grpc;
+import static com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.GCE_PRODUCTION_NAME_AFTER_2016;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -37,13 +38,16 @@
import com.google.api.core.ApiFunction;
import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.Builder;
import com.google.api.gax.rpc.HeaderProvider;
+import com.google.api.gax.rpc.TransportChannel;
import com.google.api.gax.rpc.TransportChannelProvider;
import com.google.api.gax.rpc.mtls.AbstractMtlsTransportChannelTest;
import com.google.api.gax.rpc.mtls.MtlsProvider;
+import com.google.auth.Credentials;
import com.google.auth.oauth2.CloudShellCredentials;
import com.google.auth.oauth2.ComputeEngineCredentials;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.truth.Truth;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.alts.ComputeEngineChannelBuilder;
@@ -57,16 +61,40 @@
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.SetEnvironmentVariable;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.threeten.bp.Duration;
class InstantiatingGrpcChannelProviderTest extends AbstractMtlsTransportChannelTest {
+ private static final String DEFAULT_ENDPOINT = "test.googleapis.com:443";
+ private static String originalOSName;
+ private ComputeEngineCredentials computeEngineCredentials;
+
+ @BeforeAll
+ public static void setupClass() {
+ originalOSName = System.getProperty("os.name");
+ }
+
+ @BeforeEach
+ public void setup() throws IOException {
+ System.setProperty("os.name", "Linux");
+ computeEngineCredentials = Mockito.mock(ComputeEngineCredentials.class);
+ }
+
+ @AfterEach
+ public void cleanup() {
+ System.setProperty("os.name", originalOSName);
+ }
@Test
void testEndpoint() {
@@ -300,7 +328,7 @@ void testDirectPathWithGDUEndpoint() {
InstantiatingGrpcChannelProvider.newBuilder()
.setAttemptDirectPath(true)
.setAttemptDirectPathXds()
- .setEndpoint("test.googleapis.com:443")
+ .setEndpoint(DEFAULT_ENDPOINT)
.build();
assertThat(provider.canUseDirectPathWithUniverseDomain()).isTrue();
}
@@ -322,7 +350,7 @@ void testDirectPathXdsEnabled() throws IOException {
InstantiatingGrpcChannelProvider.newBuilder()
.setAttemptDirectPath(true)
.setAttemptDirectPathXds()
- .setEndpoint("test.googleapis.com:443")
+ .setEndpoint(DEFAULT_ENDPOINT)
.build();
assertThat(provider.isDirectPathXdsEnabled()).isTrue();
@@ -552,13 +580,16 @@ void testLogDirectPathMisconfigAttrempDirectPathNotSet() throws Exception {
.setEndpoint("localhost:8080")
.build();
- provider.getTransportChannel();
+ TransportChannel transportChannel = provider.getTransportChannel();
assertThat(logHandler.getAllMessages())
.contains(
"DirectPath is misconfigured. Please set the attemptDirectPath option along with the"
+ " attemptDirectPathXds option.");
InstantiatingGrpcChannelProvider.LOG.removeHandler(logHandler);
+
+ transportChannel.close();
+ transportChannel.awaitTermination(10, TimeUnit.SECONDS);
}
@Test
@@ -584,16 +615,19 @@ void testLogDirectPathMisconfigWrongCredential() throws Exception {
.setAttemptDirectPath(true)
.setHeaderProvider(Mockito.mock(HeaderProvider.class))
.setExecutor(Mockito.mock(Executor.class))
- .setEndpoint("test.googleapis.com:443")
+ .setEndpoint(DEFAULT_ENDPOINT)
.build();
- provider.getTransportChannel();
+ TransportChannel transportChannel = provider.getTransportChannel();
assertThat(logHandler.getAllMessages())
.contains(
"DirectPath is misconfigured. Please make sure the credential is an instance of"
+ " com.google.auth.oauth2.ComputeEngineCredentials .");
InstantiatingGrpcChannelProvider.LOG.removeHandler(logHandler);
+
+ transportChannel.close();
+ transportChannel.awaitTermination(10, TimeUnit.SECONDS);
}
@Test
@@ -607,10 +641,10 @@ void testLogDirectPathMisconfigNotOnGCE() throws Exception {
.setAllowNonDefaultServiceAccount(true)
.setHeaderProvider(Mockito.mock(HeaderProvider.class))
.setExecutor(Mockito.mock(Executor.class))
- .setEndpoint("test.googleapis.com:443")
+ .setEndpoint(DEFAULT_ENDPOINT)
.build();
- provider.getTransportChannel();
+ TransportChannel transportChannel = provider.getTransportChannel();
if (!InstantiatingGrpcChannelProvider.isOnComputeEngine()) {
assertThat(logHandler.getAllMessages())
@@ -618,6 +652,138 @@ void testLogDirectPathMisconfigNotOnGCE() throws Exception {
"DirectPath is misconfigured. DirectPath is only available in a GCE environment.");
}
InstantiatingGrpcChannelProvider.LOG.removeHandler(logHandler);
+
+ transportChannel.close();
+ transportChannel.awaitTermination(10, TimeUnit.SECONDS);
+ }
+
+ @Test
+ public void canUseDirectPath_happyPath() {
+ InstantiatingGrpcChannelProvider provider =
+ InstantiatingGrpcChannelProvider.newBuilder()
+ .setAttemptDirectPath(true)
+ .setCredentials(computeEngineCredentials)
+ .setEndpoint(DEFAULT_ENDPOINT)
+ .setSystemProductName(GCE_PRODUCTION_NAME_AFTER_2016)
+ .build();
+ Truth.assertThat(provider.canUseDirectPath()).isTrue();
+ }
+
+ @Test
+ @SetEnvironmentVariable(
+ key = InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH,
+ value = "true")
+ public void canUseDirectPath_directPathEnvVarDisabled() {
+ InstantiatingGrpcChannelProvider provider =
+ InstantiatingGrpcChannelProvider.newBuilder()
+ .setAttemptDirectPath(true)
+ .setCredentials(computeEngineCredentials)
+ .setEndpoint(DEFAULT_ENDPOINT)
+ .setSystemProductName(GCE_PRODUCTION_NAME_AFTER_2016)
+ .build();
+ Truth.assertThat(provider.canUseDirectPath()).isFalse();
+ }
+
+ @Test
+ public void canUseDirectPath_directPathEnvVarNotSet_attemptDirectPathIsTrue() {
+ InstantiatingGrpcChannelProvider provider =
+ InstantiatingGrpcChannelProvider.newBuilder()
+ .setAttemptDirectPath(true)
+ .setCredentials(computeEngineCredentials)
+ .setEndpoint(DEFAULT_ENDPOINT)
+ .setSystemProductName(GCE_PRODUCTION_NAME_AFTER_2016)
+ .build();
+ Truth.assertThat(provider.canUseDirectPath()).isTrue();
+ }
+
+ @Test
+ public void canUseDirectPath_directPathEnvVarNotSet_attemptDirectPathIsFalse() {
+ InstantiatingGrpcChannelProvider provider =
+ InstantiatingGrpcChannelProvider.newBuilder()
+ .setAttemptDirectPath(false)
+ .setCredentials(computeEngineCredentials)
+ .setEndpoint(DEFAULT_ENDPOINT)
+ .setSystemProductName(GCE_PRODUCTION_NAME_AFTER_2016)
+ .build();
+ Truth.assertThat(provider.canUseDirectPath()).isFalse();
+ }
+
+ @Test
+ @SetEnvironmentVariable(
+ key = InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH,
+ value = "false")
+ public void canUseDirectPath_nonComputeCredentials() {
+ Credentials credentials = Mockito.mock(Credentials.class);
+ InstantiatingGrpcChannelProvider provider =
+ InstantiatingGrpcChannelProvider.newBuilder()
+ .setAttemptDirectPath(true)
+ .setCredentials(credentials)
+ .setEndpoint(DEFAULT_ENDPOINT)
+ .setSystemProductName(GCE_PRODUCTION_NAME_AFTER_2016)
+ .build();
+ Truth.assertThat(provider.canUseDirectPath()).isFalse();
+ }
+
+ @Test
+ @SetEnvironmentVariable(
+ key = InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH,
+ value = "false")
+ public void canUseDirectPath_isNotOnComputeEngine_invalidOsNameSystemProperty() {
+ System.setProperty("os.name", "Not Linux");
+ InstantiatingGrpcChannelProvider provider =
+ InstantiatingGrpcChannelProvider.newBuilder()
+ .setAttemptDirectPath(true)
+ .setCredentials(computeEngineCredentials)
+ .setEndpoint(DEFAULT_ENDPOINT)
+ .setSystemProductName(GCE_PRODUCTION_NAME_AFTER_2016)
+ .build();
+ Truth.assertThat(provider.canUseDirectPath()).isFalse();
+ }
+
+ @Test
+ @SetEnvironmentVariable(
+ key = InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH,
+ value = "false")
+ public void canUseDirectPath_isNotOnComputeEngine_invalidSystemProductName() {
+ InstantiatingGrpcChannelProvider provider =
+ InstantiatingGrpcChannelProvider.newBuilder()
+ .setAttemptDirectPath(true)
+ .setCredentials(computeEngineCredentials)
+ .setEndpoint(DEFAULT_ENDPOINT)
+ .setSystemProductName("testing")
+ .build();
+ Truth.assertThat(provider.canUseDirectPath()).isFalse();
+ }
+
+ @Test
+ @SetEnvironmentVariable(
+ key = InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH,
+ value = "false")
+ public void canUseDirectPath_isNotOnComputeEngine_unableToGetSystemProductName() {
+ InstantiatingGrpcChannelProvider provider =
+ InstantiatingGrpcChannelProvider.newBuilder()
+ .setAttemptDirectPath(true)
+ .setCredentials(computeEngineCredentials)
+ .setEndpoint(DEFAULT_ENDPOINT)
+ .setSystemProductName(null)
+ .build();
+ Truth.assertThat(provider.canUseDirectPath()).isFalse();
+ }
+
+ @Test
+ @SetEnvironmentVariable(
+ key = InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH,
+ value = "false")
+ public void canUseDirectPath_nonGDUUniverseDomain() {
+ String nonGDUEndpoint = "test.random.com:443";
+ InstantiatingGrpcChannelProvider provider =
+ InstantiatingGrpcChannelProvider.newBuilder()
+ .setAttemptDirectPath(true)
+ .setCredentials(computeEngineCredentials)
+ .setEndpoint(nonGDUEndpoint)
+ .setSystemProductName(GCE_PRODUCTION_NAME_AFTER_2016)
+ .build();
+ Truth.assertThat(provider.canUseDirectPath()).isFalse();
}
private static class FakeLogHandler extends Handler {
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsTracer.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsTracer.java
index 7938bde82b..abbc9138dc 100644
--- a/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsTracer.java
+++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsTracer.java
@@ -61,6 +61,7 @@ public class MetricsTracer implements ApiTracer {
"Operation has already been completed";
private Stopwatch attemptTimer;
private final Stopwatch operationTimer = Stopwatch.createStarted();
+ // These are RPC specific attributes and pertain to a specific API Trace
private final Map attributes = new HashMap<>();
private final MetricsRecorder metricsRecorder;
private final AtomicBoolean operationFinished;
diff --git a/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsTracerFactory.java b/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsTracerFactory.java
index d2b8d87fb4..3aa17bfb6c 100644
--- a/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsTracerFactory.java
+++ b/gax-java/gax/src/main/java/com/google/api/gax/tracing/MetricsTracerFactory.java
@@ -31,6 +31,8 @@
import com.google.api.core.BetaApi;
import com.google.api.core.InternalApi;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
/**
* A {@link ApiTracerFactory} to build instances of {@link MetricsTracer}.
@@ -45,13 +47,29 @@
public class MetricsTracerFactory implements ApiTracerFactory {
protected MetricsRecorder metricsRecorder;
+ /** Mapping of client attributes that are set for every MetricsTracer */
+ private final Map attributes;
+
+ /** Creates a MetricsTracerFactory with no additional client level attributes. */
public MetricsTracerFactory(MetricsRecorder metricsRecorder) {
+ this(metricsRecorder, ImmutableMap.of());
+ }
+
+ /**
+ * Pass in a Map of client level attributes which will be added to every single MetricsTracer
+ * created from the ApiTracerFactory.
+ */
+ public MetricsTracerFactory(MetricsRecorder metricsRecorder, Map attributes) {
this.metricsRecorder = metricsRecorder;
+ this.attributes = ImmutableMap.copyOf(attributes);
}
@Override
public ApiTracer newTracer(ApiTracer parent, SpanName spanName, OperationType operationType) {
- return new MetricsTracer(
- MethodName.of(spanName.getClientName(), spanName.getMethodName()), metricsRecorder);
+ MetricsTracer metricsTracer =
+ new MetricsTracer(
+ MethodName.of(spanName.getClientName(), spanName.getMethodName()), metricsRecorder);
+ attributes.forEach(metricsTracer::addAttributes);
+ return metricsTracer;
}
}
diff --git a/gax-java/gax/src/test/java/com/google/api/gax/tracing/MetricsTracerFactoryTest.java b/gax-java/gax/src/test/java/com/google/api/gax/tracing/MetricsTracerFactoryTest.java
index 16e2078bc0..d5459921e5 100644
--- a/gax-java/gax/src/test/java/com/google/api/gax/tracing/MetricsTracerFactoryTest.java
+++ b/gax-java/gax/src/test/java/com/google/api/gax/tracing/MetricsTracerFactoryTest.java
@@ -33,12 +33,16 @@
import static org.mockito.Mockito.when;
import com.google.api.gax.tracing.ApiTracerFactory.OperationType;
+import com.google.common.collect.ImmutableMap;
import com.google.common.truth.Truth;
+import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
class MetricsTracerFactoryTest {
+ private static final int DEFAULT_ATTRIBUTES_COUNT = 2;
+
@Mock private MetricsRecorder metricsRecorder;
@Mock private ApiTracer parent;
private SpanName spanName;
@@ -60,22 +64,36 @@ void testNewTracer_notNull() {
ApiTracer apiTracer = metricsTracerFactory.newTracer(parent, spanName, OperationType.Unary);
// Assert that the apiTracer created has expected type and not null
- Truth.assertThat(apiTracer).isInstanceOf(MetricsTracer.class);
Truth.assertThat(apiTracer).isNotNull();
+ Truth.assertThat(apiTracer).isInstanceOf(MetricsTracer.class);
}
@Test
- void testNewTracer_HasCorrectParameters() {
-
- // Call the newTracer method
- ApiTracer apiTracer = metricsTracerFactory.newTracer(parent, spanName, OperationType.Unary);
+ void testNewTracer_hasCorrectNumberAttributes_hasDefaultAttributes() {
+ MetricsTracer metricsTracer =
+ (MetricsTracer) metricsTracerFactory.newTracer(parent, spanName, OperationType.Unary);
+ Map attributes = metricsTracer.getAttributes();
+ Truth.assertThat(attributes.size()).isEqualTo(DEFAULT_ATTRIBUTES_COUNT);
+ Truth.assertThat(attributes.get(MetricsTracer.METHOD_NAME_ATTRIBUTE))
+ .isEqualTo("testService.testMethod");
+ Truth.assertThat(attributes.get(MetricsTracer.LANGUAGE_ATTRIBUTE))
+ .isEqualTo(MetricsTracer.DEFAULT_LANGUAGE);
+ }
- // Assert that the apiTracer created has expected type and not null
- Truth.assertThat(apiTracer).isInstanceOf(MetricsTracer.class);
- Truth.assertThat(apiTracer).isNotNull();
+ @Test
+ void testClientAttributes_additionalClientAttributes() {
+ Map clientAttributes =
+ ImmutableMap.of("attribute1", "value1", "attribute2", "value2");
+ MetricsTracerFactory metricsTracerFactory =
+ new MetricsTracerFactory(metricsRecorder, clientAttributes);
- MetricsTracer metricsTracer = (MetricsTracer) apiTracer;
- Truth.assertThat(metricsTracer.getAttributes().get("method_name"))
- .isEqualTo("testService.testMethod");
+ MetricsTracer metricsTracer =
+ (MetricsTracer) metricsTracerFactory.newTracer(parent, spanName, OperationType.Unary);
+ Map attributes = metricsTracer.getAttributes();
+ Truth.assertThat(attributes.size())
+ .isEqualTo(DEFAULT_ATTRIBUTES_COUNT + clientAttributes.size());
+ // Default attributes already tested above
+ Truth.assertThat(attributes.containsKey("attribute1")).isTrue();
+ Truth.assertThat(attributes.containsKey("attribute2")).isTrue();
}
}
diff --git a/gax-java/pom.xml b/gax-java/pom.xml
index 8737de90a3..52259b896f 100644
--- a/gax-java/pom.xml
+++ b/gax-java/pom.xml
@@ -189,6 +189,12 @@
mockito-junit-jupiter
test
+
+ org.junit-pioneer
+ junit-pioneer
+ 2.2.0
+ test
+
com.google.truth
truth
diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java
index c2ecff034d..30a9776092 100644
--- a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java
+++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelMetrics.java
@@ -35,6 +35,7 @@
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.core.ApiFuture;
import com.google.api.gax.core.NoCredentialsProvider;
+import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider;
import com.google.api.gax.retrying.RetrySettings;
import com.google.api.gax.rpc.InvalidArgumentException;
import com.google.api.gax.rpc.StatusCode.Code;
@@ -69,7 +70,9 @@
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.metrics.data.PointData;
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
+import java.io.IOException;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -100,7 +103,7 @@ class ITOtelMetrics {
private static final String OPERATION_COUNT = SERVICE_NAME + "/operation_count";
private static final String ATTEMPT_LATENCY = SERVICE_NAME + "/attempt_latency";
private static final String OPERATION_LATENCY = SERVICE_NAME + "/operation_latency";
- private static final int NUM_METRICS = 4;
+ private static final int NUM_DEFAULT_METRICS = 4;
private static final int NUM_COLLECTION_FLUSH_ATTEMPTS = 10;
private InMemoryMetricReader inMemoryMetricReader;
private EchoClient grpcClient;
@@ -272,16 +275,22 @@ private void verifyStatusAttribute(
}
}
+ /** Uses the default InMemoryMetricReader configured for showcase tests. */
+ private List getMetricDataList() throws InterruptedException {
+ return getMetricDataList(inMemoryMetricReader);
+ }
+
/**
- * Attempts to retrieve the metrics from the InMemoryMetricsReader. Sleep every second for at most
- * 10s to try and retrieve all the metrics available. If it is unable to retrieve all the metrics,
- * fail the test.
+ * Attempts to retrieve the metrics from a custom InMemoryMetricsReader. Sleep every second for at
+ * most 10s to try and retrieve all the metrics available. If it is unable to retrieve all the
+ * metrics, fail the test.
*/
- private List getMetricDataList() throws InterruptedException {
+ private List getMetricDataList(InMemoryMetricReader metricReader)
+ throws InterruptedException {
for (int i = 0; i < NUM_COLLECTION_FLUSH_ATTEMPTS; i++) {
Thread.sleep(1000L);
- List metricData = new ArrayList<>(inMemoryMetricReader.collectAllMetrics());
- if (metricData.size() == NUM_METRICS) {
+ List metricData = new ArrayList<>(metricReader.collectAllMetrics());
+ if (metricData.size() == NUM_DEFAULT_METRICS) {
return metricData;
}
}
@@ -296,19 +305,19 @@ void testGrpc_operationSucceeded_recordsMetrics() throws InterruptedException {
EchoRequest.newBuilder().setContent("test_grpc_operation_succeeded").build();
grpcClient.echo(echoRequest);
- List metricDataList = getMetricDataList();
- verifyPointDataSum(metricDataList, attemptCount);
+ List actualMetricDataList = getMetricDataList();
+ verifyPointDataSum(actualMetricDataList, attemptCount);
- Map attributeMapping =
+ Map expectedAttributes =
ImmutableMap.of(
MetricsTracer.METHOD_NAME_ATTRIBUTE,
"Echo.Echo",
MetricsTracer.LANGUAGE_ATTRIBUTE,
MetricsTracer.DEFAULT_LANGUAGE);
- verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
+ verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
List statusCountList = ImmutableList.of(new StatusCount(Code.OK));
- verifyStatusAttribute(metricDataList, statusCountList);
+ verifyStatusAttribute(actualMetricDataList, statusCountList);
}
@Disabled("https://github.com/googleapis/sdk-platform-java/issues/2503")
@@ -319,19 +328,19 @@ void testHttpJson_operationSucceeded_recordsMetrics() throws InterruptedExceptio
EchoRequest.newBuilder().setContent("test_http_operation_succeeded").build();
httpClient.echo(echoRequest);
- List metricDataList = getMetricDataList();
- verifyPointDataSum(metricDataList, attemptCount);
+ List actualMetricDataList = getMetricDataList();
+ verifyPointDataSum(actualMetricDataList, attemptCount);
- Map attributeMapping =
+ Map expectedAttributes =
ImmutableMap.of(
MetricsTracer.METHOD_NAME_ATTRIBUTE,
"google.showcase.v1beta1.Echo/Echo",
MetricsTracer.LANGUAGE_ATTRIBUTE,
MetricsTracer.DEFAULT_LANGUAGE);
- verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
+ verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
List statusCountList = ImmutableList.of(new StatusCount(Code.OK));
- verifyStatusAttribute(metricDataList, statusCountList);
+ verifyStatusAttribute(actualMetricDataList, statusCountList);
}
@Test
@@ -349,19 +358,19 @@ void testGrpc_operationCancelled_recordsMetrics() throws Exception {
Thread.sleep(1000);
blockResponseApiFuture.cancel(true);
- List metricDataList = getMetricDataList();
- verifyPointDataSum(metricDataList, attemptCount);
+ List actualMetricDataList = getMetricDataList();
+ verifyPointDataSum(actualMetricDataList, attemptCount);
- Map attributeMapping =
+ Map expectedAttributes =
ImmutableMap.of(
MetricsTracer.METHOD_NAME_ATTRIBUTE,
"Echo.Block",
MetricsTracer.LANGUAGE_ATTRIBUTE,
MetricsTracer.DEFAULT_LANGUAGE);
- verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
+ verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
List statusCountList = ImmutableList.of(new StatusCount(Code.CANCELLED));
- verifyStatusAttribute(metricDataList, statusCountList);
+ verifyStatusAttribute(actualMetricDataList, statusCountList);
}
@Disabled("https://github.com/googleapis/sdk-platform-java/issues/2503")
@@ -377,19 +386,19 @@ void testHttpJson_operationCancelled_recordsMetrics() throws Exception {
Thread.sleep(1000);
blockResponseApiFuture.cancel(true);
- List metricDataList = getMetricDataList();
- verifyPointDataSum(metricDataList, attemptCount);
+ List actualMetricDataList = getMetricDataList();
+ verifyPointDataSum(actualMetricDataList, attemptCount);
- Map attributeMapping =
+ Map expectedAttributes =
ImmutableMap.of(
MetricsTracer.METHOD_NAME_ATTRIBUTE,
"google.showcase.v1beta1.Echo/Block",
MetricsTracer.LANGUAGE_ATTRIBUTE,
MetricsTracer.DEFAULT_LANGUAGE);
- verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
+ verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
List statusCountList = ImmutableList.of(new StatusCount(Code.CANCELLED));
- verifyStatusAttribute(metricDataList, statusCountList);
+ verifyStatusAttribute(actualMetricDataList, statusCountList);
}
@Test
@@ -406,19 +415,19 @@ void testGrpc_operationFailed_recordsMetrics() throws InterruptedException {
ApiFuture blockResponseApiFuture = blockCallable.futureCall(blockRequest);
assertThrows(ExecutionException.class, blockResponseApiFuture::get);
- List metricDataList = getMetricDataList();
- verifyPointDataSum(metricDataList, attemptCount);
+ List actualMetricDataList = getMetricDataList();
+ verifyPointDataSum(actualMetricDataList, attemptCount);
- Map attributeMapping =
+ Map expectedAttributes =
ImmutableMap.of(
MetricsTracer.METHOD_NAME_ATTRIBUTE,
"Echo.Block",
MetricsTracer.LANGUAGE_ATTRIBUTE,
MetricsTracer.DEFAULT_LANGUAGE);
- verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
+ verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
List statusCountList = ImmutableList.of(new StatusCount(statusCode));
- verifyStatusAttribute(metricDataList, statusCountList);
+ verifyStatusAttribute(actualMetricDataList, statusCountList);
}
@Disabled("https://github.com/googleapis/sdk-platform-java/issues/2503")
@@ -436,19 +445,19 @@ void testHttpJson_operationFailed_recordsMetrics() throws InterruptedException {
ApiFuture blockResponseApiFuture = blockCallable.futureCall(blockRequest);
assertThrows(ExecutionException.class, blockResponseApiFuture::get);
- List metricDataList = getMetricDataList();
- verifyPointDataSum(metricDataList, attemptCount);
+ List actualMetricDataList = getMetricDataList();
+ verifyPointDataSum(actualMetricDataList, attemptCount);
- Map attributeMapping =
+ Map expectedAttributes =
ImmutableMap.of(
MetricsTracer.METHOD_NAME_ATTRIBUTE,
"google.showcase.v1beta1.Echo/Block",
MetricsTracer.LANGUAGE_ATTRIBUTE,
MetricsTracer.DEFAULT_LANGUAGE);
- verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
+ verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
List statusCountList = ImmutableList.of(new StatusCount(statusCode));
- verifyStatusAttribute(metricDataList, statusCountList);
+ verifyStatusAttribute(actualMetricDataList, statusCountList);
}
@Test
@@ -499,19 +508,19 @@ void testGrpc_attemptFailedRetriesExhausted_recordsMetrics() throws Exception {
assertThrows(UnavailableException.class, () -> grpcClient.echo(echoRequest));
- List metricDataList = getMetricDataList();
- verifyPointDataSum(metricDataList, attemptCount);
+ List actualMetricDataList = getMetricDataList();
+ verifyPointDataSum(actualMetricDataList, attemptCount);
- Map attributeMapping =
+ Map expectedAttributes =
ImmutableMap.of(
MetricsTracer.METHOD_NAME_ATTRIBUTE,
"Echo.Echo",
MetricsTracer.LANGUAGE_ATTRIBUTE,
MetricsTracer.DEFAULT_LANGUAGE);
- verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
+ verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
List statusCountList = ImmutableList.of(new StatusCount(statusCode, 3));
- verifyStatusAttribute(metricDataList, statusCountList);
+ verifyStatusAttribute(actualMetricDataList, statusCountList);
grpcClient.close();
grpcClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS);
@@ -567,19 +576,19 @@ void testHttpJson_attemptFailedRetriesExhausted_recordsMetrics() throws Exceptio
assertThrows(UnavailableException.class, () -> httpClient.echo(echoRequest));
- List metricDataList = getMetricDataList();
- verifyPointDataSum(metricDataList, attemptCount);
+ List actualMetricDataList = getMetricDataList();
+ verifyPointDataSum(actualMetricDataList, attemptCount);
- Map attributeMapping =
+ Map expectedAttributes =
ImmutableMap.of(
MetricsTracer.METHOD_NAME_ATTRIBUTE,
"google.showcase.v1beta1.Echo/Echo",
MetricsTracer.LANGUAGE_ATTRIBUTE,
MetricsTracer.DEFAULT_LANGUAGE);
- verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
+ verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
List statusCountList = ImmutableList.of(new StatusCount(statusCode, 3));
- verifyStatusAttribute(metricDataList, statusCountList);
+ verifyStatusAttribute(actualMetricDataList, statusCountList);
httpClient.close();
httpClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS);
@@ -597,19 +606,19 @@ void testGrpc_attemptPermanentFailure_recordsMetrics() throws InterruptedExcepti
assertThrows(InvalidArgumentException.class, () -> grpcClient.block(blockRequest));
- List metricDataList = getMetricDataList();
- verifyPointDataSum(metricDataList, attemptCount);
+ List actualMetricDataList = getMetricDataList();
+ verifyPointDataSum(actualMetricDataList, attemptCount);
- Map attributeMapping =
+ Map expectedAttributes =
ImmutableMap.of(
MetricsTracer.METHOD_NAME_ATTRIBUTE,
"Echo.Block",
MetricsTracer.LANGUAGE_ATTRIBUTE,
MetricsTracer.DEFAULT_LANGUAGE);
- verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
+ verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
List statusCountList = ImmutableList.of(new StatusCount(statusCode));
- verifyStatusAttribute(metricDataList, statusCountList);
+ verifyStatusAttribute(actualMetricDataList, statusCountList);
}
@Disabled("https://github.com/googleapis/sdk-platform-java/issues/2503")
@@ -625,19 +634,19 @@ void testHttpJson_attemptPermanentFailure_recordsMetrics() throws InterruptedExc
assertThrows(InvalidArgumentException.class, () -> httpClient.block(blockRequest));
- List metricDataList = getMetricDataList();
- verifyPointDataSum(metricDataList, attemptCount);
+ List actualMetricDataList = getMetricDataList();
+ verifyPointDataSum(actualMetricDataList, attemptCount);
- Map attributeMapping =
+ Map expectedAttributes =
ImmutableMap.of(
MetricsTracer.METHOD_NAME_ATTRIBUTE,
"google.showcase.v1beta1.Echo/Block",
MetricsTracer.LANGUAGE_ATTRIBUTE,
MetricsTracer.DEFAULT_LANGUAGE);
- verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
+ verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
List statusCountList = ImmutableList.of(new StatusCount(statusCode));
- verifyStatusAttribute(metricDataList, statusCountList);
+ verifyStatusAttribute(actualMetricDataList, statusCountList);
}
@Test
@@ -694,20 +703,20 @@ void testGrpc_multipleFailedAttempts_successfulOperation() throws Exception {
grpcClient.block(blockRequest);
- List metricDataList = getMetricDataList();
- verifyPointDataSum(metricDataList, attemptCount);
+ List actualMetricDataList = getMetricDataList();
+ verifyPointDataSum(actualMetricDataList, attemptCount);
- Map attributeMapping =
+ Map expectedAttributes =
ImmutableMap.of(
MetricsTracer.METHOD_NAME_ATTRIBUTE,
"Echo.Block",
MetricsTracer.LANGUAGE_ATTRIBUTE,
MetricsTracer.DEFAULT_LANGUAGE);
- verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
+ verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
List statusCountList =
ImmutableList.of(new StatusCount(Code.DEADLINE_EXCEEDED, 2), new StatusCount(Code.OK));
- verifyStatusAttribute(metricDataList, statusCountList);
+ verifyStatusAttribute(actualMetricDataList, statusCountList);
grpcClient.close();
grpcClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS);
@@ -764,18 +773,80 @@ void testHttpJson_multipleFailedAttempts_successfulOperation() throws Exception
grpcClient.block(blockRequest);
- List metricDataList = getMetricDataList();
- verifyPointDataSum(metricDataList, attemptCount);
+ List actualMetricDataList = getMetricDataList();
+ verifyPointDataSum(actualMetricDataList, attemptCount);
- Map attributeMapping =
+ Map expectedAttributes =
ImmutableMap.of(
MetricsTracer.METHOD_NAME_ATTRIBUTE,
"google.showcase.v1beta1.Echo/Block",
MetricsTracer.LANGUAGE_ATTRIBUTE,
MetricsTracer.DEFAULT_LANGUAGE);
- verifyDefaultMetricsAttributes(metricDataList, attributeMapping);
+ verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
httpClient.close();
httpClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS);
}
+
+ @Test
+ void recordsCustomAttributes() throws InterruptedException, IOException {
+ InstantiatingGrpcChannelProvider channelProvider =
+ EchoSettings.defaultGrpcTransportProviderBuilder()
+ .setChannelConfigurator(ManagedChannelBuilder::usePlaintext)
+ .build();
+
+ // Add custom attributes to be added as client level attributes
+ Map customAttributes = new HashMap<>();
+ String directpathEnabled = "directpath_enabled";
+ customAttributes.put(directpathEnabled, String.valueOf(channelProvider.canUseDirectPath()));
+ String randomAttributeKey1 = "testing";
+ String randomAttributeValue1 = "showcase";
+ String randomAttributeKey2 = "hello";
+ String randomAttributeValue2 = "world";
+ customAttributes.put(randomAttributeKey1, randomAttributeValue1);
+ customAttributes.put(randomAttributeKey2, randomAttributeValue2);
+
+ InMemoryMetricReader inMemoryMetricReader = InMemoryMetricReader.create();
+ OpenTelemetryMetricsRecorder otelMetricsRecorder =
+ createOtelMetricsRecorder(inMemoryMetricReader);
+ MetricsTracerFactory metricsTracerFactory =
+ new MetricsTracerFactory(otelMetricsRecorder, customAttributes);
+
+ EchoSettings grpcEchoSettings =
+ EchoSettings.newBuilder()
+ .setCredentialsProvider(NoCredentialsProvider.create())
+ .setTransportChannelProvider(channelProvider)
+ .setEndpoint(TestClientInitializer.DEFAULT_GRPC_ENDPOINT)
+ .build();
+
+ EchoStubSettings echoStubSettings =
+ (EchoStubSettings)
+ grpcEchoSettings
+ .getStubSettings()
+ .toBuilder()
+ .setTracerFactory(metricsTracerFactory)
+ .build();
+ EchoStub stub = echoStubSettings.createStub();
+ EchoClient grpcClient = EchoClient.create(stub);
+
+ EchoRequest echoRequest = EchoRequest.newBuilder().setContent("content").build();
+ grpcClient.echo(echoRequest);
+
+ List actualMetricDataList = getMetricDataList(inMemoryMetricReader);
+ Map expectedAttributes =
+ ImmutableMap.of(
+ MetricsTracer.METHOD_NAME_ATTRIBUTE,
+ "Echo.Echo",
+ MetricsTracer.LANGUAGE_ATTRIBUTE,
+ MetricsTracer.DEFAULT_LANGUAGE,
+ directpathEnabled,
+ "false",
+ randomAttributeKey1,
+ randomAttributeValue1,
+ randomAttributeKey2,
+ randomAttributeValue2);
+ verifyDefaultMetricsAttributes(actualMetricDataList, expectedAttributes);
+
+ inMemoryMetricReader.close();
+ }
}