1919import com .google .api .client .http .*;
2020import com .google .api .core .InternalApi ;
2121import com .google .cloud .bigquery .telemetry .BigQueryTelemetryTracer ;
22+ import com .google .common .annotations .VisibleForTesting ;
2223import io .opentelemetry .api .common .AttributeKey ;
2324import io .opentelemetry .api .trace .Span ;
2425import io .opentelemetry .api .trace .StatusCode ;
2526import io .opentelemetry .api .trace .Tracer ;
26- import org .checkerframework .checker .nullness .qual .Nullable ;
27-
2827import java .io .IOException ;
28+ import org .jspecify .annotations .Nullable ;
2929
3030/**
3131 * HttpRequestInitializer that wraps a delegate initializer, intercepts all HTTP requests, adds
@@ -49,13 +49,17 @@ public class HttpTracingRequestInitializer implements HttpRequestInitializer {
4949 public static final AttributeKey <Long > HTTP_RESPONSE_BODY_SIZE =
5050 AttributeKey .longKey ("http.response.body.size" );
5151
52+ @ VisibleForTesting static final String HTTP_RPC_SYSTEM_NAME = "http" ;
53+
5254 private final HttpRequestInitializer delegate ;
5355 private final Tracer tracer ;
54- private static final String BIGQUERY_DOMAIN = "bigquery.googleapis.com" ;
56+ private final @ Nullable String clientRootUrl ;
5557
56- public HttpTracingRequestInitializer (HttpRequestInitializer delegate , Tracer tracer ) {
58+ public HttpTracingRequestInitializer (
59+ HttpRequestInitializer delegate , Tracer tracer , @ Nullable String clientRootUrl ) {
5760 this .delegate = delegate ;
5861 this .tracer = tracer ;
62+ this .clientRootUrl = clientRootUrl ;
5963 }
6064
6165 @ Override
@@ -73,29 +77,35 @@ public void initialize(HttpRequest request) throws IOException {
7377 String host = request .getUrl ().getHost ();
7478 Integer port = request .getUrl ().getPort ();
7579
76- Long requestBodySize = getRequestBodySize (request );
77-
78- Span span = getSpan (httpMethod , url , host , port , requestBodySize );
80+ Span span = createHttpTraceSpan (httpMethod , url , host , port );
7981
8082 // Wrap the existing response interceptor
8183 HttpResponseInterceptor originalInterceptor = request .getResponseInterceptor ();
8284 request .setResponseInterceptor (
8385 response -> {
84- try {
85- addSuccessResponseToSpan (response , httpMethod , span );
86- if (originalInterceptor != null ) {
87- originalInterceptor .interceptResponse (response );
86+ if (span .isRecording ()) {
87+ try {
88+ int statusCode = response .getStatusCode ();
89+ addCommonResponseAttributesToSpan (request , response , span , httpMethod , statusCode );
90+ addSuccessResponseToSpan (response , span , statusCode );
91+ if (originalInterceptor != null ) {
92+ originalInterceptor .interceptResponse (response );
93+ }
94+ } finally {
95+ span .end ();
8896 }
89- } finally {
90- span . end ( );
97+ } else if ( originalInterceptor != null ) {
98+ originalInterceptor . interceptResponse ( response );
9199 }
92100 });
93101
94102 // Wrap the existing unsuccessful response handler
95103 HttpUnsuccessfulResponseHandler originalHandler = request .getUnsuccessfulResponseHandler ();
96104 request .setUnsuccessfulResponseHandler (
97105 (request1 , response , supportsRetry ) -> {
98- addErrorResponseToSpan (response , span );
106+ int statusCode = response .getStatusCode ();
107+ addCommonResponseAttributesToSpan (request , response , span , httpMethod , statusCode );
108+ addErrorResponseToSpan (response , span , statusCode );
99109 try {
100110 if (originalHandler != null ) {
101111 return originalHandler .handleResponse (request1 , response , supportsRetry );
@@ -105,24 +115,75 @@ public void initialize(HttpRequest request) throws IOException {
105115 addExceptionToSpan (e , span );
106116 throw e ;
107117 } finally {
108- span .end ();
118+ if (span .isRecording ()) {
119+ span .end ();
120+ }
109121 }
110122 });
111123 }
112124
125+ /** Initial HTTP trace span creation with basic attributes from request */
126+ private Span createHttpTraceSpan (String httpMethod , String url , String host , Integer port ) {
127+ // TODO: Determine span name: {method} {url.template} or {method}
128+ Span span =
129+ BigQueryTelemetryTracer .newSpanBuilder (tracer , httpMethod )
130+ .setAttribute (HTTP_REQUEST_METHOD , httpMethod )
131+ .setAttribute (URL_FULL , url )
132+ .setAttribute (BigQueryTelemetryTracer .SERVER_ADDRESS , host )
133+ .setAttribute (URL_DOMAIN , resolveUrlDomain (host ))
134+ .setAttribute (BigQueryTelemetryTracer .RPC_SYSTEM_NAME , HTTP_RPC_SYSTEM_NAME )
135+ .startSpan ();
136+
137+ // TODO: add url template && resource name
138+ if (port != null && port > 0 ) {
139+ span .setAttribute (BigQueryTelemetryTracer .SERVER_PORT , port .longValue ());
140+ }
141+ return span ;
142+ }
143+
144+ private String resolveUrlDomain (String requestHost ) {
145+ if (clientRootUrl != null ) {
146+ try {
147+ String configuredHost = new GenericUrl (clientRootUrl ).getHost ();
148+ if (configuredHost != null && !configuredHost .isEmpty ()) {
149+ return configuredHost ;
150+ }
151+ } catch (IllegalArgumentException ex ) {
152+ // Ignore malformed configured root URL and fall back to request host.
153+ }
154+ }
155+ return requestHost ;
156+ }
157+
158+ private static void addCommonResponseAttributesToSpan (
159+ HttpRequest request , HttpResponse response , Span span , String httpMethod , int statusCode ) {
160+ // This is called after we get a response as sometimes the request body size isn't available
161+ // before the response is received.
162+ addRequestBodySizeToSpan (request , span );
163+ checkForUpdatedRequestMethod (response , httpMethod , span );
164+ setResponseBodySize (response , span );
165+ span .setAttribute (HTTP_RESPONSE_STATUS_CODE , statusCode );
166+ }
167+
168+ private static void addSuccessResponseToSpan (HttpResponse response , Span span , int statusCode ) {
169+ if (statusCode >= 400 ) {
170+ addErrorResponseToSpan (response , span , statusCode );
171+ } else {
172+ span .setStatus (StatusCode .OK );
173+ }
174+ }
175+
113176 private static void addExceptionToSpan (IOException e , Span span ) {
114177 span .recordException (e );
178+ String message = e .getMessage ();
179+ String statusMessage = message != null ? message : e .getClass ().getName ();
115180 span .setAttribute (BigQueryTelemetryTracer .EXCEPTION_TYPE , e .getClass ().getName ());
116181 span .setAttribute (BigQueryTelemetryTracer .ERROR_TYPE , e .getClass ().getSimpleName ());
117- span .setAttribute (
118- BigQueryTelemetryTracer .STATUS_MESSAGE ,
119- e .getMessage () != null ? e .getMessage () : e .getClass ().getName ());
120- span .setStatus (StatusCode .ERROR , e .getMessage ());
182+ span .setAttribute (BigQueryTelemetryTracer .STATUS_MESSAGE , statusMessage );
183+ span .setStatus (StatusCode .ERROR , statusMessage );
121184 }
122185
123- private static void addErrorResponseToSpan (HttpResponse response , Span span ) {
124- int statusCode = response .getStatusCode ();
125- span .setAttribute (HTTP_RESPONSE_STATUS_CODE , statusCode );
186+ private static void addErrorResponseToSpan (HttpResponse response , Span span , int statusCode ) {
126187 String errorMessage = "HTTP " + statusCode ;
127188 try {
128189 String statusMessage = response .getStatusMessage ();
@@ -137,63 +198,39 @@ private static void addErrorResponseToSpan(HttpResponse response, Span span) {
137198 span .setStatus (StatusCode .ERROR , errorMessage );
138199 }
139200
140- private static void addSuccessResponseToSpan (
141- HttpResponse response , String httpMethod , Span span ) {
142- String actualMethod = response .getRequest ().getRequestMethod ();
143- if (actualMethod != null && httpMethod == null ) {
144- span .updateName (actualMethod );
145- span .setAttribute (HTTP_REQUEST_METHOD , actualMethod );
146- }
147- int statusCode = response .getStatusCode ();
148- span .setAttribute (HTTP_RESPONSE_STATUS_CODE , statusCode );
201+ private static void addRequestBodySizeToSpan (HttpRequest request , Span span ) {
202+ Long requestBodySize = null ;
149203 try {
150- long contentLength = response .getHeaders ().getContentLength ();
151- if (contentLength > 0 ) {
152- span .setAttribute (HTTP_RESPONSE_BODY_SIZE , contentLength );
204+ HttpContent content = request .getContent ();
205+
206+ if (content != null ) {
207+ requestBodySize = content .getLength ();
153208 }
154209 } catch (Exception e ) {
155210 // Ignore - body size not available
156211 }
157- if (statusCode >= 400 ) {
158- addErrorResponseToSpan (response , span );
159- } else {
160- span .setStatus (StatusCode .OK );
161- }
162- }
163-
164- private Span getSpan (
165- String httpMethod , String url , String host , Integer port , Long requestBodySize ) {
166- // TODO: Determine span name: {method} {url.template} or {method}
167- Span span =
168- BigQueryTelemetryTracer .newSpanBuilder (tracer , httpMethod )
169- // OpenTelemetry semantic convention attributes
170- .setAttribute (HTTP_REQUEST_METHOD , httpMethod )
171- .setAttribute (URL_FULL , url )
172- .setAttribute (BigQueryTelemetryTracer .SERVER_ADDRESS , host )
173- .setAttribute (URL_DOMAIN , BIGQUERY_DOMAIN )
174- .setAttribute (BigQueryTelemetryTracer .RPC_SYSTEM_NAME , "http" )
175- .startSpan ();
176-
177- // TODO: add url template && resource name
178- if (port != null && port > 0 ) {
179- span .setAttribute (BigQueryTelemetryTracer .SERVER_PORT , port .longValue ());
180- }
181- if (requestBodySize != null && requestBodySize > 0 ) {
212+ if (requestBodySize != null ) {
182213 span .setAttribute (HTTP_REQUEST_BODY_SIZE , requestBodySize );
183214 }
184- return span ;
185215 }
186216
187- private static @ Nullable Long getRequestBodySize (HttpRequest request ) {
188- Long requestBodySize = null ;
217+ private static void setResponseBodySize (HttpResponse response , Span span ) {
189218 try {
190- HttpContent content = request . getContent ();
191- if (content != null ) {
192- requestBodySize = content . getLength ( );
219+ long contentLength = response . getHeaders (). getContentLength ();
220+ if (contentLength > 0 ) {
221+ span . setAttribute ( HTTP_RESPONSE_BODY_SIZE , contentLength );
193222 }
194223 } catch (Exception e ) {
195224 // Ignore - body size not available
196225 }
197- return requestBodySize ;
226+ }
227+
228+ private static void checkForUpdatedRequestMethod (
229+ HttpResponse response , String httpMethod , Span span ) {
230+ String actualMethod = response .getRequest ().getRequestMethod ();
231+ if (actualMethod != null && httpMethod == null ) {
232+ span .updateName (actualMethod );
233+ span .setAttribute (HTTP_REQUEST_METHOD , actualMethod );
234+ }
198235 }
199236}
0 commit comments