Skip to content

Commit 7fdf9ff

Browse files
feat(bigquery): add url.full attribute to span tracing (#12176)
This PR adds the url.full attribute to span tracing. It redacts any credentials from the url itself, including query params using a predefined set of key words standardized by rust [here](https://github.com/googleapis/google-cloud-rust/blob/main/src/gax-internal/src/observability/http_tracing.rs#L28). [example trace](https://screenshot.googleplex.com/o5W9GdWgsoruxNj.png) --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent 5f31ded commit 7fdf9ff

File tree

2 files changed

+73
-7
lines changed

2 files changed

+73
-7
lines changed

java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/telemetry/HttpTracingRequestInitializer.java

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ public class HttpTracingRequestInitializer implements HttpRequestInitializer {
5252

5353
@VisibleForTesting static final String HTTP_RPC_SYSTEM_NAME = "http";
5454

55+
private static final java.util.Set<String> REDACTED_QUERY_PARAMETERS =
56+
com.google.common.collect.ImmutableSet.of(
57+
"AWSAccessKeyId", "Signature", "sig", "X-Goog-Signature", "upload_id");
58+
5559
private final HttpRequestInitializer delegate;
5660
private final Tracer tracer;
5761

@@ -74,9 +78,7 @@ public void initialize(HttpRequest request) throws IOException {
7478
// No active span to exists, skip instrumentation
7579
return;
7680
}
77-
String host = request.getUrl().getHost();
78-
int port = request.getUrl().getPort();
79-
addInitialHttpAttributesToSpan(span, host, port);
81+
addInitialHttpAttributesToSpan(span, request);
8082

8183
HttpResponseInterceptor originalInterceptor = request.getResponseInterceptor();
8284
request.setResponseInterceptor(
@@ -99,14 +101,16 @@ public void initialize(HttpRequest request) throws IOException {
99101
}
100102

101103
/** Add initial HTTP attributes to the existing active span */
102-
private void addInitialHttpAttributesToSpan(Span span, String host, Integer port) {
104+
private void addInitialHttpAttributesToSpan(Span span, HttpRequest request) {
103105
BigQueryTelemetryTracer.addCommonAttributeToSpan(span);
104106
span.setAttribute(BigQueryTelemetryTracer.RPC_SYSTEM_NAME, HTTP_RPC_SYSTEM_NAME);
107+
String host = request.getUrl().getHost();
105108
span.setAttribute(BigQueryTelemetryTracer.SERVER_ADDRESS, host);
106-
if (port != null && port > 0) {
107-
span.setAttribute(BigQueryTelemetryTracer.SERVER_PORT, port.longValue());
109+
int port = request.getUrl().getPort();
110+
if (port > 0) {
111+
span.setAttribute(BigQueryTelemetryTracer.SERVER_PORT, (long) port);
108112
}
109-
// TODO add full sanitized url, url domain, request method
113+
span.setAttribute(URL_FULL, getSanitizedUrl(request));
110114
}
111115

112116
private static void addCommonResponseAttributesToSpan(
@@ -136,4 +140,20 @@ static void addResponseBodySizeToSpan(HttpResponse response, Span span) {
136140
}
137141
// TODO handle chunked responses
138142
}
143+
144+
/** Removes credentials from URL. */
145+
private static String getSanitizedUrl(HttpRequest request) {
146+
GenericUrl clone = request.getUrl().clone();
147+
// redact credentials sent as part of the address
148+
if (clone.getUserInfo() != null) {
149+
clone.setUserInfo("REDACTED:REDACTED");
150+
}
151+
// redact credentials passed as query params
152+
for (String key : clone.keySet()) {
153+
if (REDACTED_QUERY_PARAMETERS.contains(key)) {
154+
clone.put(key, "REDACTED");
155+
}
156+
}
157+
return clone.build();
158+
}
139159
}

java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/telemetry/HttpTracingRequestInitializerTest.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
package com.google.cloud.bigquery.telemetry;
1818

1919
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
import static org.junit.jupiter.api.Assertions.assertFalse;
2021
import static org.junit.jupiter.api.Assertions.assertNull;
22+
import static org.junit.jupiter.api.Assertions.assertTrue;
2123
import static org.mockito.ArgumentMatchers.any;
2224
import static org.mockito.ArgumentMatchers.anyBoolean;
2325
import static org.mockito.Mockito.mock;
@@ -201,6 +203,49 @@ public void testUnsuccessfulResponseHandlerSetsErrorIfNoOriginal() throws IOExce
201203
closeAndVerifySpanData(401, "GET", -1, -1);
202204
}
203205

206+
@Test
207+
public void testUrlQueryParametersAreRedacted() throws IOException {
208+
HttpTransport transport = createTransport();
209+
String urlWithQuery = BASE_URL + "?upload_id=secret_id&Signature=secret_sig&keep_me=ok";
210+
HttpRequest request = buildGetRequest(transport, initializer, urlWithQuery);
211+
212+
HttpResponse response = request.execute();
213+
response.disconnect();
214+
215+
spanScope.close();
216+
parentSpan.end();
217+
218+
List<SpanData> spans = spanExporter.getFinishedSpanItems();
219+
assertEquals(1, spans.size());
220+
SpanData span = spans.get(0);
221+
String urlFull = span.getAttributes().get(HttpTracingRequestInitializer.URL_FULL);
222+
223+
assertTrue(urlFull.contains("upload_id=REDACTED"));
224+
assertTrue(urlFull.contains("Signature=REDACTED"));
225+
assertTrue(urlFull.contains("keep_me=ok"));
226+
}
227+
228+
@Test
229+
public void testUrlCredentialsAreRedacted() throws IOException {
230+
HttpTransport transport = createTransport();
231+
String credUrl = "https://user:pass@bigquery.googleapis.com/bigquery/v2/projects/test/datasets";
232+
HttpRequest request = buildGetRequest(transport, initializer, credUrl);
233+
234+
HttpResponse response = request.execute();
235+
response.disconnect();
236+
237+
spanScope.close();
238+
parentSpan.end();
239+
240+
List<SpanData> spans = spanExporter.getFinishedSpanItems();
241+
assertEquals(1, spans.size());
242+
SpanData span = spans.get(0);
243+
String urlFull = span.getAttributes().get(HttpTracingRequestInitializer.URL_FULL);
244+
245+
assertFalse(urlFull.contains("user:pass"));
246+
assertTrue(urlFull.contains("REDACTED:REDACTED@"));
247+
}
248+
204249
@Test
205250
public void testAddRequestBodySizeToSpan_ExceptionHandled() throws IOException {
206251
HttpContent content = mock(HttpContent.class);
@@ -369,6 +414,7 @@ private void closeAndVerifySpanData(
369414
Version.VERSION, span.getAttributes().get(BigQueryTelemetryTracer.GCP_CLIENT_VERSION));
370415
assertEquals(
371416
method, span.getAttributes().get(HttpTracingRequestInitializer.HTTP_REQUEST_METHOD));
417+
assertEquals(BASE_URL, span.getAttributes().get(HttpTracingRequestInitializer.URL_FULL));
372418
if (requestBodySize >= 0) {
373419
assertEquals(
374420
requestBodySize,

0 commit comments

Comments
 (0)