Skip to content

Commit 0f6f9b5

Browse files
authored
fix(storage): add App Hub storage.googleapis.com prefix to destination.id (#14527)
## Description Fixes the formatting of the `gcp.resource.destination.id` OpenTelemetry span attribute emitted by the Storage SDK to comply with the Cloud Trace App Hub URI extractor. The previous implementation emitted bare `projects/{project}/buckets/{bucket}` paths, which are rejected by Cloud Trace. This PR ensures the resource payload explicitly starts with `//storage.googleapis.com/` (e.g. `//storage.googleapis.com/projects/{project}/buckets/{bucket}`). Follows up on PR #13248.
1 parent 98c2073 commit 0f6f9b5

3 files changed

Lines changed: 19 additions & 8 deletions

File tree

‎java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/AcoSpanBuilder.java‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ final class AcoSpanBuilder implements SpanBuilder {
3636
private static final String MULTI_REGION = "multi-region";
3737
private static final String DUAL_REGION = "dual-region";
3838
private static final String PLACEHOLDER_BUCKET_LOCATION = "global";
39-
private static final String PLACEHOLDER_RESOURCE_PREFIX = "projects/_/buckets/";
39+
private static final String PLACEHOLDER_RESOURCE_PREFIX =
40+
"//storage.googleapis.com/projects/_/buckets/";
4041

4142
private final SpanBuilder delegate;
4243
private final OtelStorageDecorator parent;
@@ -184,7 +185,7 @@ static Tuple<String, String> fetch(Storage delegate, String bucketName) {
184185
String projectId = bucket.getProject() != null ? bucket.getProject().toString() : null;
185186
String resource;
186187
if (projectId != null && !projectId.isEmpty()) {
187-
resource = "projects/" + projectId + "/buckets/" + bucketName;
188+
resource = "//storage.googleapis.com/projects/" + projectId + "/buckets/" + bucketName;
188189
} else {
189190
resource = PLACEHOLDER_RESOURCE_PREFIX + bucketName;
190191
}

‎java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/ITOpenTelemetryTest.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ public void testAcoForbiddenBucketFallbackAttributes() throws Exception {
185185
assertAll(
186186
() ->
187187
assertThat(getAttributeValue(getSpan2, "gcp.resource.destination.id"))
188-
.isEqualTo("projects/_/buckets/test"),
188+
.isEqualTo("//storage.googleapis.com/projects/_/buckets/test"),
189189
() ->
190190
assertThat(getAttributeValue(getSpan2, "gcp.resource.destination.location"))
191191
.isEqualTo("global"));

‎java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/OtelStorageDecoratorAcoUnitTest.java‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public void testAcoSuccessFlow() throws Exception {
8484

8585
BucketMetadataCache.BucketMetadata meta = osd.acoContext.getCache().get("success-bucket");
8686
assertNotNull(meta);
87-
assertEquals("projects/12345/buckets/success-bucket", meta.resource);
87+
assertEquals("//storage.googleapis.com/projects/12345/buckets/success-bucket", meta.resource);
8888
assertEquals("us-east1", meta.location);
8989
assertFalse(meta.fetchPending);
9090
}
@@ -176,7 +176,7 @@ public void testAco403ForbiddenFlow() throws Exception {
176176
// Forbidden -> Fallback values retained with pending = false (Do Not Retry)
177177
BucketMetadataCache.BucketMetadata meta = osd.acoContext.getCache().get("forbidden-bucket");
178178
assertNotNull(meta);
179-
assertEquals("projects/_/buckets/forbidden-bucket", meta.resource);
179+
assertEquals("//storage.googleapis.com/projects/_/buckets/forbidden-bucket", meta.resource);
180180
assertEquals("global", meta.location);
181181
assertFalse(meta.fetchPending);
182182
}
@@ -232,7 +232,11 @@ public void testAcoAcoSpanEndSkipsPending() throws Exception {
232232
// Manually put a pending placeholder entry
233233
osd.acoContext
234234
.getCache()
235-
.put("pending-bucket", "projects/_/buckets/pending-bucket", "global", true);
235+
.put(
236+
"pending-bucket",
237+
"//storage.googleapis.com/projects/_/buckets/pending-bucket",
238+
"global",
239+
true);
236240

237241
Span mockSpan = mock(Span.class);
238242
AcoSpan acoSpan = new AcoSpan(mockSpan, "pending-bucket", osd);
@@ -257,7 +261,11 @@ public void testAcoAcoSpanEndAppliesResolved() throws Exception {
257261
// Manually put a resolved non-pending entry
258262
osd.acoContext
259263
.getCache()
260-
.put("resolved-bucket", "projects/123/buckets/resolved-bucket", "us-east1", false);
264+
.put(
265+
"resolved-bucket",
266+
"//storage.googleapis.com/projects/123/buckets/resolved-bucket",
267+
"us-east1",
268+
false);
261269

262270
Span mockSpan = mock(Span.class);
263271
AcoSpan acoSpan = new AcoSpan(mockSpan, "resolved-bucket", osd);
@@ -266,7 +274,9 @@ public void testAcoAcoSpanEndAppliesResolved() throws Exception {
266274

267275
// Verify OTel span attributes were set successfully
268276
Mockito.verify(mockSpan)
269-
.setAttribute("gcp.resource.destination.id", "projects/123/buckets/resolved-bucket");
277+
.setAttribute(
278+
"gcp.resource.destination.id",
279+
"//storage.googleapis.com/projects/123/buckets/resolved-bucket");
270280
Mockito.verify(mockSpan).setAttribute("gcp.resource.destination.location", "us-east1");
271281
}
272282
}

0 commit comments

Comments
 (0)