diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java index 4d7d836dea..d1455fcd6b 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java @@ -35,6 +35,7 @@ import com.google.api.gax.rpc.ApiCallContext; import com.google.api.gax.rpc.ApiClientHeaderProvider; import com.google.api.gax.rpc.ApiException; +import com.google.api.gax.rpc.FixedHeaderProvider; import com.google.api.gax.rpc.HeaderProvider; import com.google.api.gax.rpc.InstantiatingWatchdogProvider; import com.google.api.gax.rpc.OperationCallable; @@ -70,6 +71,7 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.RateLimiter; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.iam.v1.GetIamPolicyRequest; @@ -232,6 +234,8 @@ private void awaitTermination() throws InterruptedException { private static final int DEFAULT_TIMEOUT_SECONDS = 30 * 60; private static final int DEFAULT_PERIOD_SECONDS = 10; private static final int GRPC_KEEPALIVE_SECONDS = 2 * 60; + private static final String USER_AGENT_KEY = "user-agent"; + private static final String CLIENT_LIBRARY_LANGUAGE = "spanner-java"; // TODO(weiranf): Remove this temporary endpoint once DirectPath goes to public beta. private static final String DIRECT_PATH_ENDPOINT = "aa423245250f2bbf.sandbox.googleapis.com:443"; @@ -297,9 +301,20 @@ public GapicSpannerRpc(final SpannerOptions options) { .build(); HeaderProvider mergedHeaderProvider = options.getMergedHeaderProvider(internalHeaderProvider); + Map headersWithUserAgent = + ImmutableMap.builder() + .put( + USER_AGENT_KEY, + CLIENT_LIBRARY_LANGUAGE + + "/" + + GaxProperties.getLibraryVersion(GapicSpannerRpc.class)) + .putAll(mergedHeaderProvider.getHeaders()) + .build(); + final HeaderProvider headerProviderWithUserAgent = + FixedHeaderProvider.create(headersWithUserAgent); this.metadataProvider = SpannerMetadataProvider.create( - mergedHeaderProvider.getHeaders(), + headerProviderWithUserAgent.getHeaders(), internalHeaderProviderBuilder.getResourceHeaderKey()); this.callCredentialsProvider = options.getCallCredentialsProvider(); this.compressorName = options.getCompressorName(); @@ -338,7 +353,7 @@ public GapicSpannerRpc(final SpannerOptions options) { options.getInterceptorProvider(), SpannerInterceptorProvider.createDefault())) .withEncoding(compressorName)) - .setHeaderProvider(mergedHeaderProvider); + .setHeaderProvider(headerProviderWithUserAgent); // TODO(weiranf): Set to true by default once DirectPath goes to public beta. if (shouldAttemptDirectPath()) { @@ -478,8 +493,10 @@ private static void checkEmulatorConnection( throw SpannerExceptionFactory.newSpannerException( ErrorCode.UNAVAILABLE, String.format( - "The environment variable SPANNER_EMULATOR_HOST has been set to %s, but no running emulator could be found at that address.\n" - + "Did you forget to start the emulator, or to unset the environment variable?", + "The environment variable SPANNER_EMULATOR_HOST has been set to %s, but no running" + + " emulator could be found at that address.\n" + + "Did you forget to start the emulator, or to unset the environment" + + " variable?", emulatorHost)); } }