Skip to content

Commit 0a865bf

Browse files
feat(observability): implement url.domain attribute (#12316)
This Pull Request implements the `url.domain` observability attribute in the `gax-java` library. This attribute records the 'nominal' domain where the request is intended to go (e.g., `service.googleapis.com`), distinct from `server.address` which reflects the actual destination (which could be `localhost` or a proxy). The attribute is now recorded in both attempt-level spans and metrics for both gRPC and HTTP/JSON transports, provided that the `serviceName` is available in the `ClientContext`.
1 parent 167722d commit 0a865bf

File tree

6 files changed

+80
-14
lines changed

6 files changed

+80
-14
lines changed

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/rpc/ClientContext.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,8 @@ public static Builder newBuilder() {
152152
.setTracerFactory(BaseApiTracerFactory.getInstance())
153153
.setQuotaProjectId(null)
154154
.setGdchApiAudience(null)
155-
// Attempt to create an empty, non-functioning EndpointContext by default. This is
155+
// Attempt to create an empty, non-functioning EndpointContext by default. This
156+
// is
156157
// not exposed to the user via getters/setters.
157158
.setEndpointContext(EndpointContext.getDefaultInstance());
158159
}
@@ -185,8 +186,10 @@ public static ClientContext create(StubSettings settings) throws IOException {
185186
String settingsGdchApiAudience = settings.getGdchApiAudience();
186187
boolean usingGDCH = credentials instanceof GdchCredentials;
187188
if (usingGDCH) {
188-
// Can only determine if the GDC-H is being used via the Credentials. The Credentials object
189-
// is resolved in the ClientContext and must be passed to the EndpointContext. Rebuild the
189+
// Can only determine if the GDC-H is being used via the Credentials. The
190+
// Credentials object
191+
// is resolved in the ClientContext and must be passed to the EndpointContext.
192+
// Rebuild the
190193
// endpointContext only on GDC-H flows.
191194
endpointContext = endpointContext.withGDCH();
192195
// Resolve the new endpoint with the GDC-H flow
@@ -199,16 +202,20 @@ public static ClientContext create(StubSettings settings) throws IOException {
199202
}
200203

201204
if (settings.getQuotaProjectId() != null && credentials != null) {
202-
// If the quotaProjectId is set, wrap original credentials with correct quotaProjectId as
205+
// If the quotaProjectId is set, wrap original credentials with correct
206+
// quotaProjectId as
203207
// QuotaProjectIdHidingCredentials.
204-
// Ensure that a custom set quota project id takes priority over one detected by credentials.
208+
// Ensure that a custom set quota project id takes priority over one detected by
209+
// credentials.
205210
// Avoid the backend receiving possibly conflict values of quotaProjectId
206211
credentials = new QuotaProjectIdHidingCredentials(credentials);
207212
}
208213

209214
TransportChannelProvider transportChannelProvider = settings.getTransportChannelProvider();
210-
// After needsExecutor and StubSettings#setExecutorProvider are deprecated, transport channel
211-
// executor can only be set from TransportChannelProvider#withExecutor directly, and a provider
215+
// After needsExecutor and StubSettings#setExecutorProvider are deprecated,
216+
// transport channel
217+
// executor can only be set from TransportChannelProvider#withExecutor directly,
218+
// and a provider
212219
// will have a default executor if it needs one.
213220
if (transportChannelProvider.needsExecutor() && settings.getExecutorProvider() != null) {
214221
transportChannelProvider =
@@ -271,11 +278,13 @@ public static ClientContext create(StubSettings settings) throws IOException {
271278
if (watchdogProvider != null && watchdogProvider.shouldAutoClose()) {
272279
backgroundResources.add(watchdog);
273280
}
281+
274282
ApiTracerContext apiTracerContext =
275283
ApiTracerContext.newBuilder()
276284
.setServerAddress(endpointContext.resolvedServerAddress())
277285
.setServerPort(endpointContext.resolvedServerPort())
278286
.setLibraryMetadata(settings.getLibraryMetadata())
287+
.setUrlDomain(endpointContext.getUrlDomain())
279288
.setServiceName(endpointContext.serviceName())
280289
.build();
281290
ApiTracerFactory apiTracerFactory = settings.getTracerFactory();

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/rpc/EndpointContext.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,14 @@ public static EndpointContext getDefaultInstance() {
140140
@Nullable
141141
public abstract Integer resolvedServerPort();
142142

143+
@Nullable
144+
String getUrlDomain() {
145+
if (!Strings.isNullOrEmpty(serviceName()) && !Strings.isNullOrEmpty(resolvedUniverseDomain())) {
146+
return serviceName() + "." + resolvedUniverseDomain();
147+
}
148+
return null;
149+
}
150+
143151
public abstract Builder toBuilder();
144152

145153
public static Builder newBuilder() {

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/ApiTracerContext.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ public Map<String, Object> getAttemptAttributes() {
212212
attributes.put(
213213
ObservabilityAttributes.DESTINATION_RESOURCE_ID_ATTRIBUTE, destinationResourceId());
214214
}
215+
if (!Strings.isNullOrEmpty(urlDomain())) {
216+
attributes.put(ObservabilityAttributes.URL_DOMAIN_ATTRIBUTE, urlDomain());
217+
}
215218
return attributes;
216219
}
217220

@@ -232,10 +235,10 @@ Map<String, Object> getMetricsAttributes() {
232235
if (!Strings.isNullOrEmpty(fullMethodName())) {
233236
attributes.put(ObservabilityAttributes.GRPC_RPC_METHOD_ATTRIBUTE, fullMethodName());
234237
}
238+
if (!Strings.isNullOrEmpty(urlDomain())) {
239+
attributes.put(ObservabilityAttributes.URL_DOMAIN_ATTRIBUTE, urlDomain());
240+
}
235241
if (transport() == Transport.HTTP) {
236-
if (!Strings.isNullOrEmpty(urlDomain())) {
237-
attributes.put(ObservabilityAttributes.URL_DOMAIN_ATTRIBUTE, urlDomain());
238-
}
239242
if (!Strings.isNullOrEmpty(httpPathTemplate())) {
240243
attributes.put(ObservabilityAttributes.URL_TEMPLATE_ATTRIBUTE, httpPathTemplate());
241244
}

sdk-platform-java/gax-java/gax/src/test/java/com/google/api/gax/rpc/EndpointContextTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,4 +698,29 @@ void endpointContextBuild_resolvesInvalidEndpointAndPort() throws Exception {
698698
Truth.assertThat(endpointContext.resolvedServerAddress()).isNull();
699699
Truth.assertThat(endpointContext.resolvedServerPort()).isNull();
700700
}
701+
702+
@Test
703+
void getUrlDomain_success() throws IOException {
704+
EndpointContext endpointContext = defaultEndpointContextBuilder.build();
705+
Truth.assertThat(endpointContext.getUrlDomain()).isEqualTo("test.googleapis.com");
706+
}
707+
708+
@Test
709+
void getUrlDomain_nullServiceName() throws IOException {
710+
EndpointContext endpointContext = defaultEndpointContextBuilder.setServiceName(null).build();
711+
Truth.assertThat(endpointContext.getUrlDomain()).isNull();
712+
}
713+
714+
@Test
715+
void getUrlDomain_emptyServiceName() throws IOException {
716+
EndpointContext endpointContext = defaultEndpointContextBuilder.setServiceName("").build();
717+
Truth.assertThat(endpointContext.getUrlDomain()).isNull();
718+
}
719+
720+
@Test
721+
void getUrlDomain_nonGDUUniverseDomain() throws IOException {
722+
EndpointContext endpointContext =
723+
defaultEndpointContextBuilder.setUniverseDomain("random.com").build();
724+
Truth.assertThat(endpointContext.getUrlDomain()).isEqualTo("test.random.com");
725+
}
701726
}

sdk-platform-java/gax-java/gax/src/test/java/com/google/api/gax/tracing/ApiTracerContextTest.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,19 @@ void testGetAttemptAttributes_emptyStrings() {
186186
assertThat(attributes).isEmpty();
187187
}
188188

189+
@Test
190+
void testGetAttemptAttributes_urlDomain() {
191+
ApiTracerContext context =
192+
ApiTracerContext.newBuilder()
193+
.setLibraryMetadata(LibraryMetadata.empty())
194+
.setUrlDomain("test-domain.com")
195+
.build();
196+
Map<String, Object> attributes = context.getAttemptAttributes();
197+
198+
assertThat(attributes)
199+
.containsEntry(ObservabilityAttributes.URL_DOMAIN_ATTRIBUTE, "test-domain.com");
200+
}
201+
189202
@Test
190203
void testGetAttemptAttributes_serviceName() {
191204
ApiTracerContext context =
@@ -317,7 +330,8 @@ void testGetMetricsAttributes_urlDomain_notHttp() {
317330
.build();
318331
Map<String, Object> attributes = context.getMetricsAttributes();
319332

320-
assertThat(attributes).doesNotContainKey(ObservabilityAttributes.URL_DOMAIN_ATTRIBUTE);
333+
assertThat(attributes)
334+
.containsEntry(ObservabilityAttributes.URL_DOMAIN_ATTRIBUTE, "test-domain.com");
321335
}
322336

323337
@Test

sdk-platform-java/java-showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITOtelTracing.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,6 @@ void testTracing_successfulEcho_grpc() throws Exception {
119119
SpanTracerFactory tracingFactory = new SpanTracerFactory(openTelemetrySdk);
120120

121121
EchoSettings grpcEchoSettings = createEchoSettings(false);
122-
123122
EchoStub stub = createStubWithServiceName(grpcEchoSettings, tracingFactory);
124123

125124
try (EchoClient client = EchoClient.create(stub)) {
@@ -176,8 +175,12 @@ void testTracing_successfulEcho_grpc() throws Exception {
176175
.getAttributes()
177176
.get(AttributeKey.stringKey(ObservabilityAttributes.GRPC_RPC_METHOD_ATTRIBUTE)))
178177
.isEqualTo("google.showcase.v1beta1.Echo/Echo");
178+
assertThat(
179+
attemptSpan
180+
.getAttributes()
181+
.get(AttributeKey.stringKey(ObservabilityAttributes.URL_DOMAIN_ATTRIBUTE)))
182+
.isEqualTo("showcase.googleapis.com");
179183
assertThat(attemptSpan.getInstrumentationScopeInfo().getName()).isEqualTo(SHOWCASE_ARTIFACT);
180-
// {x-version-update-end}
181184
}
182185
}
183186

@@ -186,7 +189,6 @@ void testTracing_successfulEcho_httpjson() throws Exception {
186189
SpanTracerFactory tracingFactory = new SpanTracerFactory(openTelemetrySdk);
187190

188191
EchoSettings httpJsonEchoSettings = createEchoSettings(true);
189-
190192
EchoStub stub = createStubWithServiceName(httpJsonEchoSettings, tracingFactory);
191193

192194
try (EchoClient client = EchoClient.create(stub)) {
@@ -244,6 +246,11 @@ void testTracing_successfulEcho_httpjson() throws Exception {
244246
.getAttributes()
245247
.get(AttributeKey.stringKey(ObservabilityAttributes.HTTP_URL_TEMPLATE_ATTRIBUTE)))
246248
.isEqualTo("v1beta1/echo:echo");
249+
assertThat(
250+
attemptSpan
251+
.getAttributes()
252+
.get(AttributeKey.stringKey(ObservabilityAttributes.URL_DOMAIN_ATTRIBUTE)))
253+
.isEqualTo("showcase.googleapis.com");
247254
assertThat(
248255
attemptSpan
249256
.getAttributes()

0 commit comments

Comments
 (0)